Repository: web-devkits/Wasmnizer-ts Branch: main Commit: 228cb803eb71 Files: 499 Total size: 2.9 MB Directory structure: gitextract_uq_06no8/ ├── .c8rc.json ├── .clang-format ├── .clang-tidy ├── .devcontainer/ │ ├── .dockerignore │ ├── Dockerfile │ ├── devcontainer.json │ └── docker-compose.yml ├── .dockerignore ├── .eslintignore ├── .eslintrc.json ├── .github/ │ └── workflows/ │ ├── benchmark.yml │ ├── build_llvm_libraries.yml │ ├── libdyntype_ci.yaml │ ├── ts2wasm_aot.yml │ ├── ts2wasm_ci.yaml │ ├── ts2wasm_macos.yml │ └── ts2wasm_windows.yml ├── .gitignore ├── .mocharc.json ├── .prettierignore ├── .prettierrc.json ├── ATTRIBUTIONS.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── cli/ │ ├── options.json │ └── ts2wasm.ts ├── config/ │ ├── config_mgr.ts │ └── log4js.ts ├── doc/ │ ├── additonal_sematic_check.md │ ├── architecture.md │ ├── dev_environment.md │ ├── developer-guide/ │ │ ├── any_object.md │ │ ├── basic_concepts.md │ │ ├── class.md │ │ ├── expose_host_API.md │ │ ├── fallback.md │ │ ├── feature_list.md │ │ ├── function.md │ │ ├── generic.md │ │ ├── import_export.md │ │ ├── index.md │ │ ├── interface.md │ │ └── wasmType_usage.md │ ├── environment_variables.md │ ├── faq.md │ ├── getting_started.md │ ├── libdyntype_api_spec.md │ ├── libstruct_indirect_api_spec.md │ ├── standard-library/ │ │ ├── array.md │ │ ├── console.md │ │ ├── index.md │ │ ├── math.md │ │ └── string.md │ └── validation_strategy.md ├── example/ │ └── app-framework/ │ ├── CMakeLists.txt │ ├── app/ │ │ ├── connection.ts │ │ ├── request_handler.ts │ │ ├── request_sender.ts │ │ └── timer.ts │ ├── build.sh │ ├── lib/ │ │ ├── attr_container.ts │ │ ├── connection.ts │ │ ├── request.ts │ │ ├── timer.ts │ │ └── utils.ts │ ├── profiles/ │ │ ├── host-aot/ │ │ │ └── wamr_config_wasmnizer_ts.cmake │ │ └── host-interp/ │ │ └── wamr_config_wasmnizer_ts.cmake │ └── src/ │ ├── iwasm_main.c │ └── main.c ├── lib/ │ └── builtin/ │ ├── builtin_name.ts │ ├── lib.type.d.ts │ └── lib_builtin.ts ├── package.json ├── runtime-library/ │ ├── CMakeLists.txt │ ├── LICENSE │ ├── README.md │ ├── build.sh │ ├── deps/ │ │ ├── .gitignore │ │ └── download.sh │ ├── libdyntype/ │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ ├── dynamic-qjs/ │ │ │ ├── context.c │ │ │ ├── fallback.c │ │ │ ├── object.c │ │ │ ├── pure_dynamic.h │ │ │ └── type.h │ │ ├── dynamic-simple/ │ │ │ ├── README.md │ │ │ ├── context.c │ │ │ ├── dyn-value/ │ │ │ │ ├── README.md │ │ │ │ ├── class/ │ │ │ │ │ ├── date.c │ │ │ │ │ ├── dyn_class.c │ │ │ │ │ ├── dyn_class.h │ │ │ │ │ ├── object.c │ │ │ │ │ └── string.c │ │ │ │ ├── dyn_value.c │ │ │ │ └── dyn_value.h │ │ │ ├── fallback.c │ │ │ ├── object.c │ │ │ └── pure_dynamic.h │ │ ├── extref/ │ │ │ ├── extref.c │ │ │ └── extref.h │ │ ├── lib_dyntype_wrapper.c │ │ ├── libdyntype.c │ │ ├── libdyntype.cmake │ │ ├── libdyntype.h │ │ ├── libdyntype_export.h │ │ └── test/ │ │ ├── CMakeLists.txt │ │ ├── dump.cc │ │ ├── object_property_test.cc │ │ ├── operator_test.cc │ │ ├── prototype_test.cc │ │ ├── test_app.h │ │ └── types_test.cc │ ├── main.c │ ├── stdlib/ │ │ ├── lib_array.c │ │ ├── lib_console.c │ │ └── lib_timer.c │ ├── stringref/ │ │ ├── README.md │ │ ├── stringref_qjs.c │ │ └── stringref_simple.c │ ├── struct-indirect/ │ │ ├── lib_struct_indirect.c │ │ └── lib_struct_indirect.h │ ├── utils/ │ │ ├── object_utils.c │ │ ├── object_utils.h │ │ ├── type_utils.c │ │ ├── type_utils.h │ │ ├── wamr_utils.c │ │ └── wamr_utils.h │ └── wamr_config.cmake ├── scripts/ │ ├── LICENSE.txt │ ├── copy_lib_files.js │ ├── gdb/ │ │ ├── .gitignore │ │ ├── constants.py │ │ ├── utils.py │ │ ├── wamr_dbg.py │ │ ├── wamr_exec_env.py │ │ ├── wamr_objects.py │ │ └── wamr_types.py │ ├── hooks/ │ │ └── pre-commit │ └── link_hooks.js ├── security.md ├── src/ │ ├── backend/ │ │ ├── README.md │ │ ├── binaryen/ │ │ │ ├── glue/ │ │ │ │ ├── LICENSE │ │ │ │ ├── NOTICE │ │ │ │ ├── binaryen.d.ts │ │ │ │ ├── binaryen.js │ │ │ │ ├── packType.ts │ │ │ │ ├── transform.ts │ │ │ │ └── utils.ts │ │ │ ├── index.ts │ │ │ ├── lib/ │ │ │ │ ├── array_utils.ts │ │ │ │ ├── dyntype/ │ │ │ │ │ └── utils.ts │ │ │ │ ├── env_init.ts │ │ │ │ ├── init_builtin_api.ts │ │ │ │ └── interface/ │ │ │ │ └── meta.c │ │ │ ├── memory.ts │ │ │ ├── utils.ts │ │ │ ├── wasm_expr_gen.ts │ │ │ ├── wasm_stmt_gen.ts │ │ │ └── wasm_type_gen.ts │ │ └── index.ts │ ├── dump_ast.ts │ ├── error.ts │ ├── expression.ts │ ├── frontend.ts │ ├── import_resolve.ts │ ├── log.ts │ ├── scope.ts │ ├── semantic_check.ts │ ├── semantics/ │ │ ├── builder_context.ts │ │ ├── builtin.ts │ │ ├── dump.ts │ │ ├── expression_builder.ts │ │ ├── flatten.ts │ │ ├── index.ts │ │ ├── internal.ts │ │ ├── ir/ │ │ │ ├── data_pool.ts │ │ │ ├── function.ts │ │ │ ├── ir_context.ts │ │ │ ├── ircode.ts │ │ │ └── irmodule.ts │ │ ├── predefined_types.ts │ │ ├── runtime.ts │ │ ├── semantics_nodes.ts │ │ ├── statement_builder.ts │ │ ├── type_creator.ts │ │ ├── value.ts │ │ └── value_types.ts │ ├── statement.ts │ ├── type.ts │ ├── utils.ts │ └── variable.ts ├── tests/ │ ├── benchmark/ │ │ ├── README.md │ │ ├── any_basic_type_access.js │ │ ├── any_basic_type_access.ts │ │ ├── any_complex_type_access.js │ │ ├── any_complex_type_access.ts │ │ ├── array_access.js │ │ ├── array_access.ts │ │ ├── array_access_i32.js │ │ ├── array_access_i32.ts │ │ ├── binarytrees_class.js │ │ ├── binarytrees_class.ts │ │ ├── binarytrees_interface.js │ │ ├── binarytrees_interface.ts │ │ ├── class_access.js │ │ ├── class_access.ts │ │ ├── class_allocation.js │ │ ├── class_allocation.ts │ │ ├── compile_benchmark.js │ │ ├── fibonacci.js │ │ ├── fibonacci.ts │ │ ├── interface_access_field_fastpath.js │ │ ├── interface_access_field_fastpath.ts │ │ ├── interface_access_field_slowpath.js │ │ ├── interface_access_field_slowpath.ts │ │ ├── interface_access_method_fastpath.js │ │ ├── interface_access_method_fastpath.ts │ │ ├── interface_access_method_slowpath.js │ │ ├── interface_access_method_slowpath.ts │ │ ├── mandelbrot.js │ │ ├── mandelbrot.ts │ │ ├── mandelbrot_i32.js │ │ ├── mandelbrot_i32.ts │ │ ├── merkletrees.js │ │ ├── merkletrees.ts │ │ ├── nbody_class.js │ │ ├── nbody_class.ts │ │ ├── nbody_interface.js │ │ ├── nbody_interface.ts │ │ ├── quicksort.js │ │ ├── quicksort.ts │ │ ├── quicksort_float.js │ │ ├── quicksort_float.ts │ │ ├── run.sh │ │ ├── run_benchmark.js │ │ ├── run_benchmark_on_chrome.html │ │ ├── run_benchmark_on_chrome.js │ │ ├── spectral_norm.js │ │ ├── spectral_norm.ts │ │ ├── spectral_norm_i32.js │ │ └── spectral_norm_i32.ts │ ├── samples/ │ │ ├── add_shorthand_prop.ts │ │ ├── any_as_string.ts │ │ ├── any_binary_operation.ts │ │ ├── any_box_any.ts │ │ ├── any_box_array.ts │ │ ├── any_box_boolean.ts │ │ ├── any_box_interface.ts │ │ ├── any_box_null.ts │ │ ├── any_box_number.ts │ │ ├── any_box_obj.ts │ │ ├── any_box_obj_as_return_value.ts │ │ ├── any_box_string.ts │ │ ├── any_box_undefind.ts │ │ ├── any_cast_class.ts │ │ ├── any_func_call.ts │ │ ├── any_nested_literal_value.ts │ │ ├── any_obj_prop_get.ts │ │ ├── any_obj_prop_get_as_string.ts │ │ ├── any_obj_prop_set.ts │ │ ├── any_operate_with_static.ts │ │ ├── array_concat.ts │ │ ├── array_contain_closure.ts │ │ ├── array_contain_func.ts │ │ ├── array_copyWithin.ts │ │ ├── array_elem_get.ts │ │ ├── array_elem_set.ts │ │ ├── array_every.ts │ │ ├── array_fill.ts │ │ ├── array_filter.ts │ │ ├── array_find.ts │ │ ├── array_findIndex.ts │ │ ├── array_foreach.ts │ │ ├── array_includes.ts │ │ ├── array_indexOf.ts │ │ ├── array_join.ts │ │ ├── array_lastIndexOf.ts │ │ ├── array_map.ts │ │ ├── array_nested_array.ts │ │ ├── array_nested_literal.ts │ │ ├── array_nested_literal_array.ts │ │ ├── array_new_array.ts │ │ ├── array_new_array_number.ts │ │ ├── array_new_array_string.ts │ │ ├── array_new_literal_any.ts │ │ ├── array_new_literal_boolean.ts │ │ ├── array_new_literal_number.ts │ │ ├── array_new_literal_string.ts │ │ ├── array_pop.ts │ │ ├── array_push.ts │ │ ├── array_reduce.ts │ │ ├── array_reduceRight.ts │ │ ├── array_reverse.ts │ │ ├── array_shift.ts │ │ ├── array_slice.ts │ │ ├── array_some.ts │ │ ├── array_sort.ts │ │ ├── array_specialization.ts │ │ ├── array_splice.ts │ │ ├── array_unshift.ts │ │ ├── array_wasm_basic_type.ts │ │ ├── arraybuffer_basic.ts │ │ ├── assign_different_type.ts │ │ ├── auto_box_unbox.ts │ │ ├── base_function_call.ts │ │ ├── block_closure.ts │ │ ├── block_inner_block.ts │ │ ├── boolean_basic.ts │ │ ├── boolean_logic_operator.ts │ │ ├── boolean_not.ts │ │ ├── builtin_array.ts │ │ ├── builtin_console.ts │ │ ├── builtin_math.ts │ │ ├── builtin_string.ts │ │ ├── call_expression_function_hoisting.ts │ │ ├── call_expression_get_value.ts │ │ ├── call_expression_param.ts │ │ ├── cast_any_to_static.ts │ │ ├── class_accessor.ts │ │ ├── class_basic.ts │ │ ├── class_direct_call.ts │ │ ├── class_extend.ts │ │ ├── class_field_assign.ts │ │ ├── class_field_is_array.ts │ │ ├── class_static_prop.ts │ │ ├── class_type.ts │ │ ├── class_vtable_accessor.ts │ │ ├── class_vtable_call.ts │ │ ├── closure_basic.ts │ │ ├── closure_first_class_func.ts │ │ ├── closure_inner_func.ts │ │ ├── closure_set_ctx_value.ts │ │ ├── comments_not_entry.ts │ │ ├── comments_with_export.ts │ │ ├── comments_with_import.ts │ │ ├── complexType_case1.ts │ │ ├── complexType_case2.ts │ │ ├── complexType_case3.ts │ │ ├── complexType_case4.ts │ │ ├── complexType_case5.ts │ │ ├── compound_assignment_operator.ts │ │ ├── dataview_basic.ts │ │ ├── decimalization.ts │ │ ├── declare_class.ts │ │ ├── declare_func.ts │ │ ├── declare_namespace.ts │ │ ├── declare_var.ts │ │ ├── default_param.ts │ │ ├── enum.ts │ │ ├── exception_catch_error.ts │ │ ├── exception_custom_error.ts │ │ ├── exception_throw_error.ts │ │ ├── exception_try_structure.ts │ │ ├── export_alias_identifier.ts │ │ ├── export_alias_imported_identifier.ts │ │ ├── export_class.ts │ │ ├── export_expression_number_literal.ts │ │ ├── export_expression_obj_literal.ts │ │ ├── export_from/ │ │ │ ├── classB.ts │ │ │ └── libA/ │ │ │ ├── index.ts │ │ │ └── lib/ │ │ │ └── classA.ts │ │ ├── export_func.ts │ │ ├── export_func2.ts │ │ ├── export_func_invoked.ts │ │ ├── export_implicit_type.ts │ │ ├── export_namespace.ts │ │ ├── export_re_export.ts │ │ ├── export_type.ts │ │ ├── export_var.ts │ │ ├── expression_binary.ts │ │ ├── expression_binary_select.ts │ │ ├── expression_condition.ts │ │ ├── expression_unary.ts │ │ ├── fallback_quickjs.ts │ │ ├── fallback_quickjs_Date.ts │ │ ├── fallback_quickjs_JSON.ts │ │ ├── for_in.ts │ │ ├── for_of.ts │ │ ├── func_cast_interface.ts │ │ ├── function_declaration.ts │ │ ├── function_expression.ts │ │ ├── function_scope_var.ts │ │ ├── generic_class.ts │ │ ├── generic_func.ts │ │ ├── generic_param.ts │ │ ├── global_generics_function.ts │ │ ├── global_value.ts │ │ ├── global_variables.ts │ │ ├── if_statement_case1.ts │ │ ├── ignore_parameter_in_variable.ts │ │ ├── import_alias_identifier.ts │ │ ├── import_alias_reexport_identifier.ts │ │ ├── import_alias_scope_identifier.ts │ │ ├── import_class.ts │ │ ├── import_expression.ts │ │ ├── import_func.ts │ │ ├── import_implicit_type.ts │ │ ├── import_namespace.ts │ │ ├── import_type.ts │ │ ├── import_var.ts │ │ ├── infc_assign_class.ts │ │ ├── infc_assign_infc.ts │ │ ├── infc_assign_obj.ts │ │ ├── infc_case18.ts │ │ ├── infc_field_assign.ts │ │ ├── infc_method.ts │ │ ├── infc_parameter.ts │ │ ├── infc_return_value.ts │ │ ├── infc_with_array.ts │ │ ├── inner_generics_function.ts │ │ ├── instanceof.ts │ │ ├── lib.ts │ │ ├── loop_do_while.ts │ │ ├── loop_for.ts │ │ ├── loop_while.ts │ │ ├── map_callback.ts │ │ ├── mixed_type.ts │ │ ├── module-case/ │ │ │ ├── export_declare.ts │ │ │ ├── export_normal.ts │ │ │ └── import_case1.ts │ │ ├── module_start_A.ts │ │ ├── module_start_B.ts │ │ ├── module_start_C.ts │ │ ├── namespace_func.ts │ │ ├── namespace_generics_function.ts │ │ ├── namespace_nest.ts │ │ ├── namespace_var.ts │ │ ├── nest_generic.ts │ │ ├── non_null_expression.ts │ │ ├── null_type_case1.ts │ │ ├── obj_literal.ts │ │ ├── obj_method_call.ts │ │ ├── obj_property_access.ts │ │ ├── obj_property_dynamic_access.ts │ │ ├── op_ref_type.ts │ │ ├── optional_method.ts │ │ ├── optional_param.ts │ │ ├── optional_property_access.ts │ │ ├── parenthesized_expression_case1.ts │ │ ├── percentToken.ts │ │ ├── primitiveType.ts │ │ ├── promise_chain.ts │ │ ├── promise_constructor.ts │ │ ├── promise_immediate.ts │ │ ├── promise_throw.ts │ │ ├── prototype.ts │ │ ├── rec_types.ts │ │ ├── ref_type_cmp.ts │ │ ├── rest_param_interface.ts │ │ ├── rest_param_number.ts │ │ ├── return_statement.ts │ │ ├── sample_cases.test.ts │ │ ├── scoped_variables.ts │ │ ├── shorthand_prop_assign.ts │ │ ├── spread_operator.ts │ │ ├── string_binary_operation.ts │ │ ├── string_or.ts │ │ ├── string_type.ts │ │ ├── switch_case_statement.ts │ │ ├── this_in_closure.ts │ │ ├── this_in_method.ts │ │ ├── toString.ts │ │ ├── top_level_statements.ts │ │ ├── tuple.ts │ │ ├── typealias.ts │ │ ├── typeof.ts │ │ ├── unary_operator.ts │ │ ├── undefined_test.ts │ │ ├── union_assign.ts │ │ ├── union_field_get.ts │ │ ├── union_func_call.ts │ │ ├── unsigned_value.ts │ │ ├── wasmType_basic.ts │ │ ├── wasmType_heapType.ts │ │ └── wasmType_in_otherType.ts │ ├── unit/ │ │ ├── comment.test.ts │ │ ├── expression.test.ts │ │ ├── scope.test.ts │ │ ├── statement.test.ts │ │ ├── type.test.ts │ │ └── variable.test.ts │ └── utils/ │ └── test_helper.ts ├── tools/ │ └── validate/ │ ├── run_module/ │ │ ├── import_object.js │ │ ├── readme.md │ │ ├── run_module_on_chrome.html │ │ ├── run_module_on_chrome.js │ │ └── run_module_on_node.js │ └── wamr/ │ ├── .gitignore │ ├── README.md │ ├── create_validation_items.ts │ ├── index.ts │ ├── package.json │ ├── tsconfig.json │ └── validation.json ├── tsconfig.json └── tsconfig.release.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .c8rc.json ================================================ { "all": true, "include": [ "src/**/*.ts" ], "exclude": [ "src/backend/binaryen/glue/*.ts" ], "reporter":[ "text-summary", "html", "lcovonly" ], "report-dir": ".test_output/coverage" } ================================================ FILE: .clang-format ================================================ # using [clang-formt-12 options](https://releases.llvm.org/12.0.0/tools/clang/docs/ClangFormatStyleOptions.html) RawStringFormats: - Language: Cpp Delimiters: - c - C - cc - CC - cpp - Cpp - CPP - 'c++' - 'C++' - h - hpp CanonicalDelimiter: '' BasedOnStyle: Mozilla Language: Cpp BasedOnStyle: Mozilla # 6.1 IndentWidth: 4 ContinuationIndentWidth: 4 # 6.2 TabWidth: 4 UseTab: Never # 6.3 ColumnLimit: 80 # 6.9 AlignAfterOpenBracket: Align BinPackArguments: true BinPackParameters: true # 6.10 BreakBeforeBraces: Custom BraceWrapping: AfterCaseLabel: true AfterClass: true AfterControlStatement: false AfterEnum: false AfterFunction: true AfterNamespace: false AfterObjCDeclaration: false AfterStruct: false AfterUnion: false AfterExternBlock: false BeforeCatch: false BeforeElse: true IndentBraces: false SplitEmptyFunction: true SplitEmptyRecord: false SplitEmptyNamespace: true # 6.27 BreakBeforeBinaryOperators: NonAssignment # additional AlignEscapedNewlines: Left AllowAllParametersOfDeclarationOnNextLine: false AllowAllArgumentsOnNextLine: false PointerAlignment: Right SpaceAroundPointerQualifiers: After SortIncludes: false ================================================ FILE: .clang-tidy ================================================ # refer to https://clang.llvm.org/extra/clang-tidy/checks/list.html Checks: '-*, readability-identifier-naming, clang-analyzer-core.*,' WarningsAsErrors: '-*' HeaderFilterRegex: '' FormatStyle: file InheritParentConfig: false AnalyzeTemporaryDtors: false User: wamr CheckOptions: - key: readability-identifier-naming.VariableCase value: lower_case - key: readability-identifier-naming.ParameterCase value: lower_case - key: readability-identifier-naming.MacroDefinitionCase value: UPPER_CASE ================================================ FILE: .devcontainer/.dockerignore ================================================ node_modules ================================================ FILE: .devcontainer/Dockerfile ================================================ # # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # FROM node:16.16.0 ARG USERNAME=vscode ARG USER_UID=1000 ARG USER_GID=$USER_UID # node may come with an older version of npm. Ensure we have a specific npm. RUN npm install -g npm@8.11.0 ################################################################################ # Install prerequisites RUN apt update RUN apt install -y \ lsb-release vim \ curl wget RUN apt install -y \ build-essential cmake g++-multilib libgcc-8-dev lib32gcc-8-dev ## lsb-release allows us to find out which distro we run inside the dev container # Add binaryen RUN wget https://github.com/WebAssembly/binaryen/releases/download/version_114/binaryen-version_114-x86_64-linux.tar.gz RUN tar zxvf binaryen-version_114-x86_64-linux.tar.gz RUN ln -s binaryen-version_114/bin/wasm-as /usr/local/bin/wasm-as RUN ln -s binaryen-version_114/bin/wasm-opt /usr/local/bin/wasm-opt RUN ln -s binaryen-version_114/bin/wasm-shell /usr/local/bin/wasm-shell # Create the non-root user RUN userdel -r -f node \ && groupadd --gid $USER_GID $USERNAME \ && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \ && usermod --shell /bin/bash vscode \ # # [Optional] Add sudo support. Omit if you don't need to install software after connecting. && apt-get update \ && apt-get install -y sudo \ && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \ && chmod 0440 /etc/sudoers.d/$USERNAME USER $USERNAME ================================================ FILE: .devcontainer/devcontainer.json ================================================ { "name": "DevContainers", // dev container name "dockerComposeFile": [ "docker-compose.yml" // docker-compose configure ], // The name of the following service has to match one of the services in docker-compose.yml "service": "devcontainer", // the name of the “service” "workspaceFolder": "/ts2wasm", "extensions": [ "firsttris.vscode-jest-runner", "esbenp.prettier-vscode", "eamodio.gitlens", "ms-vscode.vscode-typescript-tslint-plugin", "ms-vsliveshare.vsliveshare", "rtbenfield.vscode-jest-test-adapter", "dbaeumer.vscode-eslint" ], "shutdownAction": "stopCompose", // tells VS Code what to do when we close the folder or VS Code "remoteUser": "vscode" } ================================================ FILE: .devcontainer/docker-compose.yml ================================================ version: '3.7' # which syntax version the file uses. services: devcontainer: image: acme/workspace:0.1 # the image tag build: . # specifies the build context for docker-compose container_name: ts2wasm-container hostname: ts2wasm-dev.local # gives the dev container a hostname it should use working_dir: /ts2wasm volumes: - ..:/ts2wasm command: sleep infinity # instructs docker which command to start when the dev container is running ================================================ FILE: .dockerignore ================================================ **/node_modules/ ================================================ FILE: .eslintignore ================================================ # cant test switch-case-statement.ts because of eslint checking, add the line temporarily tests/samples/* runtime-library/* build/* **/dist/* tools/playground/server/assets/samples/* lib/builtin/lib.type.d.ts fuzz/* ================================================ FILE: .eslintrc.json ================================================ { "parser": "@typescript-eslint/parser", "parserOptions": { "warnOnUnsupportedTypeScriptVersion": false, "ecmaVersion": 6, "sourceType": "module" }, "env": { "browser": false, "node": true, "es6": true }, "plugins": [ "@typescript-eslint/eslint-plugin", "no-null", "import", "prettier" ], "extends": [ "eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:prettier/recommended" ], "rules": { // eslint-plugin-import // deal with import/export package "import/no-extraneous-dependencies": ["error", { "optionalDependencies": false }], "@typescript-eslint/no-this-alias": ["off"], "@typescript-eslint/no-namespace": ["off"] // eslint-plugin-no-null // restricts using null as explicit values for variables or function arguments. // "no-null/no-null": "error" } } ================================================ FILE: .github/workflows/benchmark.yml ================================================ # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception name: ts2wasm_benchmark on: # will be triggered on PR events pull_request: types: - opened - synchronize paths-ignore: - "doc/**" - "README.md" # will be triggered on push events push: branches: - main paths-ignore: - "doc/**" - "README.md" # allow to be triggered manually workflow_dispatch: # Cancel any in-flight jobs for the same PR/branch so there's only one active # at a time concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: build_llvm_libraries_on_ubuntu_2204: uses: ./.github/workflows/build_llvm_libraries.yml with: os: "ubuntu-22.04" arch: "X86" execute_benchmarks: needs: [build_llvm_libraries_on_ubuntu_2204] runs-on: ${{ matrix.os }} strategy: matrix: include: - os: ubuntu-22.04 llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu_2204.outputs.cache_key }} steps: - name: checkout uses: actions/checkout@v3 - name: download wamr repo run: | ./download.sh working-directory: runtime-library/deps # since jobs.id can't contain the dot character # it is hard to use `format` to assemble the cache key - name: Get LLVM libraries id: retrieve_llvm_libs uses: actions/cache@v3 with: path: | ./runtime-library/deps/wamr-gc/core/deps/llvm/build/bin ./runtime-library/deps/wamr-gc/core/deps/llvm/build/include ./runtime-library/deps/wamr-gc/core/deps/llvm/build/lib ./runtime-library/deps/wamr-gc/core/deps/llvm/build/libexec ./runtime-library/deps/wamr-gc/core/deps/llvm/build/share key: ${{ matrix.llvm_cache_key }} - name: Quit if cache miss if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' run: echo "::error::can not get prebuilt llvm libraries" && exit 1 - name: Build wamrc run: | mkdir build && cd build cmake .. -DWAMR_BUILD_GC_BINARYEN=1 cmake --build . --config Release --parallel 4 working-directory: ./runtime-library/deps/wamr-gc/wamr-compiler - name: Install apt packages run: sudo apt update && sudo apt install g++-multilib -y - name: Build runtime run: | mkdir build && cd build cmake .. && make -j$(nproc) working-directory: runtime-library/ - name: Build quickjs run: | make -j$(nproc) working-directory: runtime-library/deps/quickjs - name: Build compilar run: | npm install npm run release - name: run benchmarks run: | node run_benchmark.js --times=5 working-directory: tests/benchmark ================================================ FILE: .github/workflows/build_llvm_libraries.yml ================================================ # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception name: Reusable workflow-build_llvm_libraries on: workflow_call: inputs: os: required: true type: string arch: required: true type: string outputs: cache_key: description: "A cached key of LLVM libraries" value: ${{ jobs.build_llvm_libraries.outputs.key}} jobs: build_llvm_libraries: runs-on: ${{ inputs.os }} outputs: key: ${{ steps.create_lib_cache_key.outputs.key}} steps: - name: checkout uses: actions/checkout@v3 - name: download wamr repo run: | ./download.sh working-directory: runtime-library/deps - name: install dependencies run: /usr/bin/env python3 -m pip install -r requirements.txt working-directory: runtime-library/deps/wamr-gc/build-scripts - name: retrive the last commit ID id: get_last_commit run: echo "last_commit=$(GH_TOKEN=${{ secrets.GITHUB_TOKEN }} /usr/bin/env python3 ./build_llvm.py --llvm-ver)" >> $GITHUB_OUTPUT working-directory: runtime-library/deps/wamr-gc/build-scripts # Bump the prefix number to evict all previous caches and # enforce a clean build, in the unlikely case that some # weird build error occur and llvm/build becomes a potential # suspect. - name: form the cache key of libraries id: create_lib_cache_key run: echo "key=0-llvm-libraries-${{ inputs.os }}-${{ inputs.arch }}-${{ steps.get_last_commit.outputs.last_commit }}" >> $GITHUB_OUTPUT - name: Cache LLVM libraries id: retrieve_llvm_libs uses: actions/cache@v3 with: path: | ./runtime-library/deps/wamr-gc/core/deps/llvm/build/bin ./runtime-library/deps/wamr-gc/core/deps/llvm/build/include ./runtime-library/deps/wamr-gc/core/deps/llvm/build/lib ./runtime-library/deps/wamr-gc/core/deps/llvm/build/libexec ./runtime-library/deps/wamr-gc/core/deps/llvm/build/share key: ${{ steps.create_lib_cache_key.outputs.key}} - uses: actions/cache@v3 with: path: ~/.ccache key: 0-ccache-${{ inputs.os }}-${{ steps.get_last_commit.outputs.last_commit }} restore-keys: | 0-ccache-${{ inputs.os }} if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' && inputs.os == 'ubuntu-20.04' - uses: actions/cache@v3 with: path: ~/.cache/ccache key: 0-ccache-${{ inputs.os }}-${{ steps.get_last_commit.outputs.last_commit }} restore-keys: | 0-ccache-${{ inputs.os }} if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' && inputs.os == 'ubuntu-22.04' - run: sudo apt install -y ccache ninja-build if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' && startsWith(inputs.os, 'ubuntu') - uses: actions/cache@v3 with: path: ~/Library/Caches/ccache key: 0-ccache-${{ inputs.os }}-${{ steps.get_last_commit.outputs.last_commit }} restore-keys: | 0-ccache-${{ inputs.os }} if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' && startsWith(inputs.os, 'macos') - run: brew install ccache ninja if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' && startsWith(inputs.os, 'macos') - name: Build LLVM libraries if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' run: /usr/bin/env python3 ./build_llvm.py --arch ${{ inputs.arch }} working-directory: runtime-library/deps/wamr-gc/build-scripts ================================================ FILE: .github/workflows/libdyntype_ci.yaml ================================================ # # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # name: libdyntype CI on: push: paths: - "runtime-library/**" pull_request: paths: - "runtime-library/**" # allow to be triggered manually workflow_dispatch: jobs: build_and_test: name: 'Libdyntype Build and Test' runs-on: ubuntu-latest defaults: run: working-directory: runtime-library/libdyntype steps: - uses: actions/checkout@v3 with: fetch-depth: 5 submodules: true - name: download dependencies run: | ./download.sh working-directory: runtime-library/deps - name: Dyntype Build run: | mkdir build && cd build cmake .. && make - name: Dyntype Test run: | cd test mkdir build && cd build cmake .. -DUNITTEST_USE_SANITIZER=1 make make test ================================================ FILE: .github/workflows/ts2wasm_aot.yml ================================================ # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception name: ts2wasm_aot on: # will be triggered on PR events pull_request: types: - opened - synchronize paths-ignore: - "doc/**" - "README.md" # will be triggered on push events push: branches: - main paths-ignore: - "doc/**" - "README.md" # allow to be triggered manually workflow_dispatch: # Cancel any in-flight jobs for the same PR/branch so there's only one active # at a time concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: build_llvm_libraries_on_ubuntu_2204: uses: ./.github/workflows/build_llvm_libraries.yml with: os: "ubuntu-22.04" arch: "X86" validate_aot_execution: needs: [build_llvm_libraries_on_ubuntu_2204] runs-on: ${{ matrix.os }} strategy: matrix: include: - os: ubuntu-22.04 llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu_2204.outputs.cache_key }} target: [ "X86_64", "X86_32" ] simple_libdyntype: [ 1, 0 ] # node-version: [10.x, 12.x, 14.x, 15.x, 16.x] # Test the latest version of Node.js plus the last two LTS versions. # node-version: # - "*" # - lts/* # - lts/-1 node-version: [16.x] steps: - name: checkout uses: actions/checkout@v3 - name: Use node version ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - name: download wamr repo run: | ./download.sh working-directory: runtime-library/deps # since jobs.id can't contain the dot character # it is hard to use `format` to assemble the cache key - name: Get LLVM libraries id: retrieve_llvm_libs uses: actions/cache@v3 with: path: | ./runtime-library/deps/wamr-gc/core/deps/llvm/build/bin ./runtime-library/deps/wamr-gc/core/deps/llvm/build/include ./runtime-library/deps/wamr-gc/core/deps/llvm/build/lib ./runtime-library/deps/wamr-gc/core/deps/llvm/build/libexec ./runtime-library/deps/wamr-gc/core/deps/llvm/build/share key: ${{ matrix.llvm_cache_key }} - name: Quit if cache miss if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' run: echo "::error::can not get prebuilt llvm libraries" && exit 1 - name: Build wamrc run: | mkdir build && cd build cmake .. -DWAMR_BUILD_GC_BINARYEN=1 cmake --build . --config Release --parallel 4 working-directory: ./runtime-library/deps/wamr-gc/wamr-compiler - name: Install apt packages run: sudo apt update && sudo apt install g++-multilib -y - name: Build runtime run: | mkdir build && cd build cmake .. -DWAMR_BUILD_TARGET=${{ matrix.target }} -DUSE_SIMPLE_LIBDYNTYPE=${{ matrix.simple_libdyntype }} -DWAMR_GC_IN_EVERY_ALLOCATION=1 -DUSE_SANITIZER=1 && make -j$(nproc) working-directory: runtime-library/ - name: Validate execution run: SIMPLE_LIBDYNTYPE=${{ matrix.simple_libdyntype }} AOT=1 TARGET_ARCH=${{ matrix.target }} npm start working-directory: tools/validate/wamr ================================================ FILE: .github/workflows/ts2wasm_ci.yaml ================================================ # # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # name: ts2wasm-compiler CI on: push: paths-ignore: - "doc/**" - "README.md" pull_request: types: - opened - synchronize paths-ignore: - "doc/**" - "README.md" # allow to be triggered manually workflow_dispatch: # Cancel any in-flight jobs for the same PR/branch so there's only one active # at a time concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: install_dependencies: runs-on: ubuntu-latest strategy: matrix: # node-version: [10.x, 12.x, 14.x, 15.x, 16.x] # Test the latest version of Node.js plus the last two LTS versions. # node-version: # - "*" # - lts/* # - lts/-1 node-version: [16.x] steps: - uses: actions/checkout@v3 with: fetch-depth: 5 - name: Use node version ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - run: npm install - name: Linter run: npx lint-staged validate_compilation: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: fetch-depth: 5 - run: npm install - name: Test compilation run: npm run test validate_execution: needs: [install_dependencies] runs-on: ubuntu-latest strategy: matrix: target: [ "X86_64", "X86_32" ] simple_libdyntype: [ 1, 0 ] steps: - uses: actions/checkout@v3 with: fetch-depth: 5 - name: Use node version 16.x uses: actions/setup-node@v3 with: node-version: 16.x - name: download dependencies run: | ./download.sh sudo apt update && sudo apt install g++-multilib -y working-directory: runtime-library/deps - name: Build runtime run: | mkdir build && cd build cmake .. -DWAMR_BUILD_TARGET=${{ matrix.target }} -DUSE_SIMPLE_LIBDYNTYPE=${{ matrix.simple_libdyntype }} -DWAMR_GC_IN_EVERY_ALLOCATION=1 -DUSE_SANITIZER=1 && make -j$(nproc) working-directory: runtime-library/ - name: Validate execution run: SIMPLE_LIBDYNTYPE=${{ matrix.simple_libdyntype }} npm start working-directory: tools/validate/wamr ================================================ FILE: .github/workflows/ts2wasm_macos.yml ================================================ # # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # name: ts2wasm-compiler CI MacOS on: push: paths-ignore: - "doc/**" - "README.md" pull_request: types: - opened - synchronize paths-ignore: - "doc/**" - "README.md" # allow to be triggered manually workflow_dispatch: # Cancel any in-flight jobs for the same PR/branch so there's only one active # at a time concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: install_dependencies: runs-on: ${{ matrix.os }} strategy: matrix: # node-version: [10.x, 12.x, 14.x, 15.x, 16.x] # Test the latest version of Node.js plus the last two LTS versions. # node-version: # - "*" # - lts/* # - lts/-1 node-version: [16.x, 18.x] os: [macos-12] steps: - uses: actions/checkout@v3 with: fetch-depth: 5 - name: Use node version ${{ matrix.node-version }} uses: actions/setup-node@v3 - run: npm install - name: Linter run: npx lint-staged validate_compilation: strategy: matrix: # node-version: [10.x, 12.x, 14.x, 15.x, 16.x] # Test the latest version of Node.js plus the last two LTS versions. # node-version: # - "*" # - lts/* # - lts/-1 os: [macos-12] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 with: fetch-depth: 5 - run: npm install - name: build the compiler run: npm run build - name: compile single file run: node build/cli/ts2wasm.js tests/samples/any_as_string.ts -o test.wasm - name: Test compilation run: npm run test validate_execution: needs: [install_dependencies] runs-on: macos-latest strategy: matrix: target: [ "X86_64" ] steps: - uses: actions/checkout@v3 with: fetch-depth: 5 - name: Use node version node-16 uses: actions/setup-node@v3 with: node-version: 16.x - name: download dependencies run: | ./download.sh working-directory: runtime-library/deps - name: Build runtime run: | mkdir build && cd build cmake .. -DWAMR_BUILD_PLATFORM="darwin" -DWAMR_BUILD_TARGET=${{ matrix.target }} -DWAMR_GC_IN_EVERY_ALLOCATION=1 && make -j$(nproc) working-directory: runtime-library/ # TODO: fix validation for MacOS CI # - name: Validate execution # run: # npm start # working-directory: tools/validate/wamr ================================================ FILE: .github/workflows/ts2wasm_windows.yml ================================================ # # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # name: ts2wasm-compiler windows on: push: paths-ignore: - "doc/**" - "README.md" pull_request: types: - opened - synchronize paths-ignore: - "doc/**" - "README.md" # allow to be triggered manually workflow_dispatch: # Cancel any in-flight jobs for the same PR/branch so there's only one active # at a time concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: install_dependencies: runs-on: windows-latest strategy: matrix: # node-version: [10.x, 12.x, 14.x, 15.x, 16.x] # Test the latest version of Node.js plus the last two LTS versions. # node-version: # - "*" # - lts/* # - lts/-1 node-version: [16.x] steps: - uses: actions/checkout@v3 with: fetch-depth: 5 - name: Use node version ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - run: npm install validate_compilation: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: fetch-depth: 5 - run: npm install - name: Test compilation run: npm run test ================================================ FILE: .gitignore ================================================ node_modules build dist out .nyc_output coverage c-backend logs lib/builtin/watFile/ logs *.swp *.wat *.wasm *.log *.swo example/app-framework/cmake-build/ example/app-framework/out/ .vscode/ tests/benchmark/compile_output/ ================================================ FILE: .mocharc.json ================================================ { "extension": ["ts"], "loader": "ts-node/esm", "spec": "tests/**/**.test.ts" } ================================================ FILE: .prettierignore ================================================ runtime-library/* tests/samples/* tools/playground/server/assets/samples/* fuzz/* ================================================ FILE: .prettierrc.json ================================================ { "printWidth": 80, "tabWidth": 4, "useTabs": false, "semi": true, "singleQuote": true, "quoteProps": "as-needed", "trailingComma": "all", "bracketSpacing": true, "arrowParens": "always", "requirePragma": false, "insertPragma": false, "proseWrap": "preserve", "endOfLine": "lf", "embeddedLanguageFormatting": "auto" } ================================================ FILE: ATTRIBUTIONS.md ================================================ Wasmnizer-ts Attributions ====================================== Wasmnizer-ts project reused some components from other open source project: - **WebAssembly Micro Runtime**: for building a runtime with WasmGC support - **QuickJS**: for implementing libdyntype to manage dynamic objects - **AssemblyScript**: use some declaration files for exposing C-APIs from binaryen.js - **TypeScript**: use some scripts for project setup - **Programming Language Benchmarks**: use some code snippets as benchmarks - **The Benchmarks Game**: use some code snippets as benchmarks ## Licenses ### WebAssembly Micro Runtime [LICENSE](./runtime-library/LICENSE) (Apache-2.0 WITH LLVM-exception) ### QuickJS [LICENSE](https://github.com/bellard/quickjs/blob/master/LICENSE) ### AssemblyScript [LICENSE](./src/backend/binaryen/glue/LICENSE) (Apache-2.0) [NOTICE](./src/backend/binaryen/glue/NOTICE) ### TypeScript [LICENSE](./scripts/LICENSE.txt) (Apache-2.0) ### Programming Language Benchmarks [LICENSE](./tests/benchmark/MIT_LICENSE.txt) (MIT) ### The Benchmarks Game [LICENSE](./tests/benchmark/BSD_LICENSE.txt) (3-Clause BSD) ## Contributors for initial version - [xujuntwt95329](https://github.com/xujuntwt95329) - **Jun Xu**, (Architect, Project champion) - [coderebot](https://github.com/coderebot) - **Junjie Dong**, (Architect) - [brucehuke](https://github.com/brucehuke) - **Ke Hu**, - [LevelCA](https://github.com/LevelCA) - **Liangyu Zhang**, - [lum1n0us](https://github.com/lum1n0us) - **Liang He**, - [penghuizhen347](https://github.com/penghuizhen347) - **Huizhen Peng**, - [Shanks0224](https://github.com/Shanks0224) - **Jing Gan**, - [shubulan](https://github.com/shubulan) - **Yulong Gao**, - [WenLY1](https://github.com/WenLY1) - **LingYun Wen**, - [wenyongh](https://github.com/wenyongh) - **Wenyong Huang**, - [xwang98](https://github.com/xwang98) - **Xin Wang**, - [yviansu](https://github.com/yviansu) - **Yihan Su**, - [zhenweijin](https://github.com/zhenweijin) - **Zhenwei Jin**, ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at CommunityCodeOfConduct AT intel DOT com. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations]. [homepage]: https://www.contributor-covenant.org [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html [Mozilla CoC]: https://github.com/mozilla/diversity [FAQ]: https://www.contributor-covenant.org/faq ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing ### License `Wasmnizer-ts` is licensed under the terms in [LICENSE](./LICENSE). By contributing to the project, you agree to the license and copyright terms therein and release your contribution under these terms. ### How to contribute We welcome contributions to Wasmnizer-ts. You can: - Log a bug or provide feedback with an [issue]. - Submit your changes directly with a [pull request]. ### Pull requests This project follows a simple workflow with contributions delivered as [pull requests](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests) (PR) against the main branch. To submit your change: - Make sure your code is in line with our coding conventions by running `npm run lint` to format the code. - Create an [issue] describing the bug the PR fixes or the feature you intend to implement. - Submit a [pull request] into the main branch. Your PR will then be reviewed by one or more maintainers. Your PR will be automatically merged (assuming no conflicts) with one approving review. Maintainers may suggest changes to a PR before approving. ### Testing #### Test compilation This will compile our samples and check if the compiler exit normally, it doesn't guarantee the correctness of the generated wasm module. ``` bash npm run test ``` #### Validate execution on WAMR See [validate/wamr](./tools/validate/wamr/README.md) for how to validate results on WAMR ### Code Formatting Code is required to be formatted with `npm run lint`. ### Sign your work Please use the sign-off line at the end of the patch. Your signature certifies that you wrote the patch or otherwise have the right to pass it on as an open-source patch. The rules are pretty simple: if you can certify the below (from [developercertificate.org](http://developercertificate.org/)): ``` Developer Certificate of Origin Version 1.1 Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 660 York Street, Suite 102, San Francisco, CA 94110 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. ``` Then you just add a line to every git commit message: Signed-off-by: Joe Smith Use your real name (sorry, no pseudonyms or anonymous contributions.) If you set your `user.name` and `user.email` git configs, you can sign your commit automatically with `git commit -s`. ================================================ FILE: LICENSE ================================================ 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 APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. 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. --- LLVM Exceptions to the Apache 2.0 License ---- As an exception, if, as a result of your compiling your source code, portions of this Software are embedded into an Object form of such source code, you may redistribute such embedded portions in such Object form without complying with the conditions of Sections 4(a), 4(b) and 4(d) of the License. In addition, if you combine or link compiled forms of this Software with software that is licensed under the GPLv2 ("Combined Software") and if a court of competent jurisdiction determines that the patent provision (Section 3), the indemnity provision (Section 9) or other Section of the License conflicts with the conditions of the GPLv2, you may retroactively and prospectively choose to deem waived or otherwise exclude such Section(s) of the License, but only in their entirety and only with respect to the Combined Software. ================================================ FILE: README.md ================================================

Wasmnizer-ts

Toolchain for compiling TypeScript to WasmGC

## Overview `Wasmnizer-ts` utilizes [WasmGC](https://github.com/WebAssembly/gc) to compile TypeScript source code into WebAssembly bytecode, and support dynamic type (such as any) through host APIs. The `Wasmnizer-ts` now supports a strict subset of TypeScript and continuously strives to accommodate more semantics. There are three components in `Wasmnizer-ts`: - `ts2wasm-compiler`: a compiler for compiling TypeScript source code into WasmGC bytecode. - `ts2wasm-stdlib`: standard library implemented in ts source code, will be compiled with application together. See [standard library](./doc/standard-library/index.md). - `ts2wasm-runtime-library`: runtime libraries for exposing host APIs required for running the generated wasm module, including: 1. `libdyntype`: support dynamic objects, see API spec [here](./doc/libdyntype_api_spec.md). We have proposed a [WASI proposal](https://github.com/WebAssembly/WASI/issues/552). 2. `libstruct-indirect`: access WasmGC struct fields through index calculated during runtime, see API spec [here](./doc/libstruct_indirect_api_spec.md). These APIs are used to emulate the behaviour of the [proposed struct.get/set_indirect opcode](https://github.com/WebAssembly/gc/issues/397). 3. `libstd`: standard library implemented in native, such as `console.log`, see [standard library](./doc/standard-library/index.md). > **Note**: **This project is highly experimental and under active development, DO NOT use in production** ## Features - **garbage collection**. `ts2wasm-compiler` leverage WebAssembly GC proposal, which can benefit from runtime's GC capability. - **optimization**. `ts2wasm-compiler` uses binaryen as backend, which can benefit from binaryen's powerful optimization capabilities. - **small footprint**. Data structures in source code is represented as WasmGC types, which avoids the requirement for `memory allocator` and `garbage collector` inside wasm module. - **static compilation**. Type information in TypeScript source code is used to create static WasmGC types, which avoids the overhead of dynamic type checking. - **dynamic typing**. `any` and other dynamic types in TypeScript source code are supported by host APIs. ## Execution environment The wasm module generated by `ts2wasm-compiler` is designed to be executed in a WasmGC runtime environment. The runtime should provide the following capabilities: - **WebAssembly proposals:** - **[WasmGC](https://github.com/WebAssembly/gc) (mandatory)**: WasmGC proposal, which is a garbage collection mechanism for WebAssembly. > Note: the GC opcode generated by binaryen is slightly different than [GC MVP](https://github.com/WebAssembly/gc/blob/main/proposals/gc/MVP.md), please see [here](https://docs.google.com/document/d/1DklC3qVuOdLHSXB5UXghM_syCh-4cMinQ50ICiXnK3Q/edit#heading=h.9dwoku9340md) for details. - **[Exception handling](https://github.com/WebAssembly/exception-handling) (required by try-catch statements)**: exception handling proposal, which adds exception handling mechanism to WebAssembly. - **[stringref](https://github.com/WebAssembly/stringref) (required by stringref feature)**: reference-typed strings proposal, provide a language independent string representation. - **APIs:** - **[libdyntype API](./doc/libdyntype_api_spec.md) (required by dynamic typing)**: APIs for supporting dynamic objects. - **[libstruct-indirect API](./doc/libstruct_indirect_api_spec.md) (required by `interface` type)**: APIs for accessing WasmGC struct fields through index calculated during runtime. - **libstd API (required by standard libraries)**: APIs for providing standard libraries from host environment. `Wasmnizer-ts` currently implemented host APIs on multiple environments: - [WebAssembly Micro Runtime (WAMR)](https://github.com/bytecodealliance/wasm-micro-runtime/tree/dev/gc_refactor): `libdyntype API`, `libstruct-indirect API` and `libstd API` - chrome browser and nodejs (20.6.1+): part of `libdyntype API` implemented with JavaScript Please see [feature list](./doc/developer-guide/feature_list.md) for supporting status of each feature. Please goto [Getting Started](./doc/getting_started.md) for how to use the project and [Introduction](./doc/developer-guide/index.md) for more details. ## Contributing We welcome contributions to Wasmnizer-ts. You can find more details in [CONTRIBUTING.md](CONTRIBUTING.md) . ## License `Wasmnizer-ts` uses the same license as LLVM: the Apache 2.0 license with the LLVM exception. See the [LICENSE](./LICENSE) file for details. Any contributions you make will be under the same license. ================================================ FILE: cli/options.json ================================================ { "version": { "category": "General", "description": "Print the compiler's version.", "alias": "v" }, "help": { "category": "General", "description": "Print the help information of this compiler.", "alias": "h" }, "output": { "category": "Output", "description": "Generate the WebAssembly binary output file (.wasm).", "alias": "o" }, "wat": { "category": "Output", "description": "Generate the WebAssembly text output file (.wat).", "default": false }, "dumpast": { "category": "Output", "description": "Dump the AST Tree", "default": false }, "validate": { "category": "Validation", "description": "Validate the specified function of the generated wasm File using WAMR." }, "baseDir": { "category": "Output", "description": "Specify base directory for storing output files.", "default": "." }, "opt": { "category": "Compile", "description": "Specify optimization level", "default": 0 }, "debug": { "category": "Compile", "description": "Enable debug mode", "default": true }, "sourceMap": { "category": "Compile", "description": "Specify source map information", "default": true }, "enableException": { "category": "Compile", "description": "Enable exception feature", "default": true }, "enableStringRef": { "category": "Compile", "description": "Enable stringref to represent typescript string", "default": true }, "entry": { "category": "Compile", "description": "Specify the entry function, default is _entry", "default": "_entry" }, "startSection": { "category": "Compile", "description": "Whether to use start section to do initialization, default is false", "default": false }, "dumpSemanticTree": { "category": "Debug", "description": "dump semantic tree, default is false", "default": false } } ================================================ FILE: cli/ts2wasm.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import minimist from 'minimist'; import cp from 'child_process'; import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; import { ParserContext } from '../src/frontend.js'; import log4js from 'log4js'; import { Logger, consoleLogger } from '../src/log.js'; import { Ts2wasmBackend } from '../src/backend/index.js'; import { WASMGen } from '../src/backend/binaryen/index.js'; import { default as logConfig } from '../config/log4js.js'; import { SyntaxError } from '../src/error.js'; import { DumpAST } from '../src/dump_ast.js'; import { ConfigMgr, setConfig } from '../config/config_mgr.js'; interface HelpMessageCategory { General: string[]; Compile: string[]; Output: string[]; Validation: string[]; Other: string[]; } function isRegularFile(filePath: string) { fs.lstat(filePath, function (err, stats) { if (stats.isSymbolicLink() || stats.nlink > 1) { throw new Error(`${filePath} is not a regular file`); } }); } function validateFilePath(filePath: string) { if (!fs.existsSync(filePath)) { throw new Error(`${filePath} not exist`); } isRegularFile(filePath); } function parseOptions() { const dirname = path.dirname(fileURLToPath(import.meta.url)); const optionPath = path.join(dirname, '..', '..', 'cli', 'options.json'); validateFilePath(optionPath); const helpFile = fs.readFileSync(optionPath, 'utf8'); const helpConfig = JSON.parse(helpFile); return helpConfig; } function showVersion() { const dirname = path.dirname(fileURLToPath(import.meta.url)); const packagePath = path.join(dirname, '..', '..', 'package.json'); validateFilePath(packagePath); const packageFile = fs.readFileSync(packagePath, 'utf8'); const packageConfig = JSON.parse(packageFile); const version = packageConfig.version; console.log('Version ' + version); process.exit(0); } function showHelp(helpConfig: any) { const printOption = { indent: 4, padding: 28, eol: '\n', }; const categories: HelpMessageCategory = { General: [], Compile: [], Output: [], Validation: [], Other: [], }; Object.keys(helpConfig).forEach((commandKey) => { const helpMessage: string[] = []; const option = helpConfig[commandKey]; let comment = ''; while (comment.length < printOption.indent) { comment += ' '; } comment += '--' + commandKey; if (option.alias) { comment += ', -' + option.alias; } while (comment.length < printOption.padding) { comment += ' '; } comment += option.description; helpMessage.push(comment); if (option.category) { const categoryKey = option.category; categories[categoryKey].push(helpMessage[0]); } else { categories['Other'].push(helpMessage[0]); } }); const optionMessage: string[] = []; Object.keys(categories).forEach((category) => { const categoryKey = category; optionMessage.push( printOption.eol + ' ' + categoryKey + printOption.eol, ); optionMessage.push(categories[categoryKey].join(printOption.eol)); }); optionMessage.join(printOption.eol); const otherMessage = [ 'EXAMPLES\n', ' ' + 'node' + ' build/cli/ts2wasm.js' + ' --help', ' ' + 'node' + ' build/cli/ts2wasm.js' + ' sample.ts' + ' --output' + ' sample.wasm', ' ' + 'node' + ' build/cli/ts2wasm.js' + ' sample.ts' + ' --output' + ' sample.wasm' + ' --wat', ' ' + 'node' + ' build/cli/ts2wasm.js' + ' sample.ts' + ' --output' + ' sample.wasm' + ' --validate' + ' functionName' + ' param1' + ' param2', '\n', 'OPTIONS', ]; const outMessage = otherMessage.concat(optionMessage); console.log(outMessage.join(printOption.eol)); process.exit(0); } export function writeFile(filename: string, contents: any, baseDir = '.') { if (!contents) { throw new Error('content is not valid'); } const dirPath = path.normalize( path.resolve(baseDir, path.dirname(filename)), ); const filePath = path.normalize( path.join(dirPath, path.basename(filename)), ); fs.mkdirSync(dirPath, { recursive: true }); fs.writeFileSync(filePath, contents); } function getAbsolutePath(filename: string, baseDir = '') { const dirPath = path.normalize( path.resolve(baseDir, path.dirname(filename)), ); const filePath = path.normalize( path.join(dirPath, path.basename(filename)), ); return filePath; } function validateByWAMR(cmdArgs: string[]) { const dirname = path.dirname(fileURLToPath(import.meta.url)); const iwasm = path.join(dirname, 'iwasm_gc'); if (!fs.existsSync(iwasm)) { throw new Error('iwasm_gc exec file not exist'); } const result = cp.execFileSync(iwasm, cmdArgs).toString(); console.log('WebAssembly output is: ' + result); } function createBackend(args: any, parserCtx: ParserContext): Ts2wasmBackend { return new WASMGen(parserCtx); } /** read configs from cli */ function readCfgFromCli(args: minimist.ParsedArgs) { const cfgs: Partial = {}; for (const key in args) { // eslint-disable-next-line no-prototype-builtins if (args.hasOwnProperty(key)) { cfgs[key as keyof ConfigMgr] = args[key]; } } setConfig(cfgs); } function main() { try { const args = minimist(process.argv.slice(2)); const optionConfig = parseOptions(); const optionKey: string[] = []; Object.keys(optionConfig).forEach((commandKey) => { optionKey.push(commandKey); if (optionConfig[commandKey].alias) { optionKey.push(optionConfig[commandKey].alias); } }); Object.keys(args).forEach((arg) => { if (arg !== '_' && optionKey.indexOf(arg) === -1) { console.warn("WARNING: Unknown option '" + arg + "'"); } }); readCfgFromCli(args); if (args.help || args.h) { showHelp(optionConfig); } if (args.version || args.v) { showVersion(); } const sourceFileList: string[] = []; const params: string[] = []; for (let i = 0; i < args._.length; i++) { const arg = args._[i]; if (typeof arg === 'string' && fs.statSync(arg).isFile()) { fs.accessSync(arg, fs.constants.R_OK); sourceFileList.push(arg); } else { params.push(arg); } } if (!sourceFileList.length) { showHelp(optionConfig); } if (!sourceFileList.length) { throw new Error('No ts file to be handled.'); } if (args.dumpast) { DumpAST(sourceFileList); return; } /* Step1: Semantic checking, construct scope tree */ const parserCtx = new ParserContext(); parserCtx.parse(sourceFileList); /* Step2: Backend codegen */ const backend = createBackend(args, parserCtx); backend.codegen(); /* Step3: output */ /* Set up specified base directory */ const baseDir = path.normalize(args.baseDir || '.'); let generatedWasmFile = ''; if (args.output || args.o) { if (args.output) { generatedWasmFile = args.output; } if (args.o) { generatedWasmFile = args.o; } if (!generatedWasmFile.endsWith('.wasm')) { throw new Error('output must be a wasm file'); } const options = { name_prefix: generatedWasmFile.split('.')[0], }; const output = backend.emitBinary(options); writeFile(generatedWasmFile, output, baseDir); console.log( "The wasm file '" + generatedWasmFile + "' has been generated.", ); if (args.sourceMap) { const sourceMap = backend.emitSourceMap(options.name_prefix); writeFile( `${options.name_prefix}.wasm.map`, sourceMap, baseDir, ); console.log( `The source map file ${options.name_prefix}.wasm.map has been generated.`, ); } if (args.validate) { const validateArgs: string[] = [ '-f', args.validate, getAbsolutePath(generatedWasmFile), ]; validateByWAMR(validateArgs.concat(params)); } if (args.wat) { let generatedWatFile = ''; if (generatedWasmFile.endsWith('.wasm')) { generatedWatFile = generatedWasmFile.replace( '.wasm', '.wat', ); } else { generatedWatFile = generatedWasmFile.concat('.wat'); } const output = backend.emitText(); writeFile(generatedWatFile, output, baseDir); console.log( "The wat file '" + generatedWatFile + "' has been generated.", ); } } else if (args.wat || args.validate) { console.warn('WARNING: No wasm file specified.'); } else { console.log(backend.emitText()); } backend.dispose(); } catch (e) { if (e instanceof SyntaxError) { /* Syntax error are reported by frontend.ts */ console.log(e.message); process.exit(1); } else { /* TODO: print line number in error message */ let errorMessage = (e).message.concat( `\nError details is in '${logConfig.appenders.errorFile.filename}'`, ); if (process.env.NODE_ENV !== 'production') { errorMessage = errorMessage.concat( `\nLog details is in '${logConfig.appenders.traceFile.filename}'`, ); } consoleLogger.error(errorMessage); Logger.error(e); log4js.shutdown(() => process.exit(1)); } } } main(); ================================================ FILE: config/config_mgr.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export interface ConfigMgr { opt: number; debug: boolean; sourceMap: boolean; enableException: boolean; enableStringRef: boolean; entry: string; startSection: boolean; dumpSemanticTree: boolean; } const defaultConfig: ConfigMgr = { opt: 0, debug: false, sourceMap: false, enableException: false, enableStringRef: true, entry: '_entry', startSection: false, dumpSemanticTree: false, }; let currentConfig: ConfigMgr = { ...defaultConfig }; export function setConfig(config: Partial): void { currentConfig = { ...currentConfig, ...config }; } export function getConfig(): ConfigMgr { return currentConfig; } ================================================ FILE: config/log4js.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import os from 'os'; import fs from 'fs'; import path from 'path'; const logDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ts2wasm-log-')); const logConfig = { appenders: { console: { type: 'console', }, traceFile: { type: 'file', filename: `${path.join(logDir, 'trace.log')}`, backups: 3, }, errorFile: { type: 'file', filename: `${path.join(logDir, 'error.log')}`, backups: 3, }, filterFile: { type: 'logLevelFilter', level: 'ERROR', appender: 'errorFile', }, }, categories: { default: { appenders: ['traceFile', 'filterFile'], level: 'trace', }, production: { appenders: ['filterFile'], level: 'error', }, console: { appenders: ['console'], level: 'error', }, }, }; export default logConfig; ================================================ FILE: doc/additonal_sematic_check.md ================================================ # Additional Sematic Check ## Motivation Due to the partially features of TypeScript and the current limitations of WebAssembly, the compiler has imposed some restrictions on the usage of TypeScript syntax. This doc records additional sematic checking information of the `Wasmnizer-ts`. ## Items The following terms outline specific additional sematic checks. | item | description | | ------------------------------- | ------------------------------------------------------------ | | nominal class | the operation between two nomial classes(different class name, or t the two classes do not have a subtyping relationship) | | closure with default parameters | innter function or closure with default parameters | | invoke any object | treat any type as an object and access its properties | | array without a specified element type | declare `Array` without a typeargument, for exampe `new Array()` | | void type value as variable, or as function argument | `const a: void = undefined` | If those rules above are triggered, an error will be throwed, the details will be dump to the log file. Note that the most of these terms are implemented in the `sematic_check` file except the check of `array without a specified element type`, because we lost the AST information when traversing `Expression`, it will be handled when parsing on sematic tree. ## Details Here are some examples and details about additional sematic checking. + the format of error message `[error type]: in [function name where the error occurred], error flag: xxx , message: xxx` For example ``` shell [closure with default parameters]: in [test|foo|bar], error flag: '2', message: 'inner function has default parameters' ``` this message shows that in function `test|foo|bar`, there occurs a inner function with defalut parameters error, its error flag is 2. + meaning of error flags **\[0]: BinaryOperationOnNominalClass** `Wasmnizer-ts` treats class type as nominal, because different named class types have distinct meanings and purposes. So operating on different types will not pass through the additional semantic checks. For example: ```typescript class Foo { x: number; constructor(xx: number) { this.x = xx; } } class Bar { x: number; constructor(xx: number) { this.x = xx; } } const f = new Foo(0); const b: Bar = f; // not pass ``` **\[1]: ReturnTypesAreNominalClass** The reason is the same as mentioned in `BinaryOperationOnNominalClass` above, here is an example: ```typescript class Foo { x: number; constructor(xx: number) { this.x = xx; } } class Bar { x: number; constructor(xx: number) { this.x = xx; } } export function baz(): Bar { return new Foo(0); // not pass } ``` **\[2]: ArgsAndParamsTypesAreNominalClass** The reason is the same as mentioned in `BinaryOperationOnNominalClass` above, here is an example: ```typescript class Foo { x: number; constructor(xx: number) { this.x = xx; } } class Bar { x: number; constructor(xx: number) { this.x = xx; } } export function baz(f: Foo) { // ... } baz(new Bar()); ``` **\[3]: ClosureOrInnerFuncHasDefaultParams** Currently in `Wasmnizer-ts`, only top-level functions are allowed to have default parameters. so inner function or closure with default parameters will not pass the checks. ```typescript function foo() { // inner function 'bar' has default parameters, so it won't pass the check. function bar(x = 10) { // } } ``` **\[4]: InvokeAnyObject** `Wasmnizer-ts` provides the capability to work with dynamic types, but it imposes restrictions on accessing properties of dynamic types and assigning them to static types. For exmaple: ```typescript class Foo { x: number; constructor(xx: number) { this.x = xx; } } const f: any = new Foo(0); const x: number = f.x; // not pass ``` it requires type casting if want to access the property of dynamic types and assign it to static types: ```typescript // ... const x: number = f.x as number; // passed ``` **\[5]: VoidTypeAsVarType** `Wasmnizer-ts` does not yet support 'void' as a variable type, so using 'void' as a variable type will not pass the check: ```typescript const v: void = undefined; // not pass function foo(v: void) { // not pass // ... } ``` **array without a specified element type** ```typescript const arr: number[] = new Array(); // not pass const arr = new Array(); // passed, `arr` has type any[] const arr: number[] = new Array(); // passed const arr = new Array(); // passed, `arr` has type number[] ``` ================================================ FILE: doc/architecture.md ================================================ ![](./img/architecture.excalidraw.png) ================================================ FILE: doc/dev_environment.md ================================================ # ts2wasm development environment A VSCode dev-container is available for this project. ### Enter the container After openning the project using VSCode, click the Green area at the left-bottom corner and select "reopen in container", it may take some time to build the docker image at the first time. ### Update the container If any modification is made to the files under `.devcontainer`, then press `crtl + shift + P` to call out the command palette, search and select `Remote-Containers: Rebuild Container` ### User in the container There is a non-root user named `vscode`, it is mapped to the first non-root user in host system, and this user has been enabled to use `sudo` without password > Note: if you want to use apt to install other softwares, you may get a timeout error if you are behind a proxy. This is because the sudo environment will clear all the environment variables including the proxy related ones. To solve this problem, simply add one line to `/etc/sudoers` **inside the container** `Defaults env_keep+="http_proxy ftp_proxy all_proxy https_proxy no_proxy"` > ``` bash > vscode@ts2wasm-dev:/ts2wasm$ sudo cat /etc/sudoers > # > # This file MUST be edited with the 'visudo' command as root. > # > # Please consider adding local content in /etc/sudoers.d/ instead of > # directly modifying this file. > # > # See the man page for details on how to write a sudoers file. > # > Defaults env_reset > Defaults mail_badpass > Defaults env_keep+="http_proxy ftp_proxy all_proxy https_proxy no_proxy" # Add this line > Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" > ``` ================================================ FILE: doc/developer-guide/any_object.md ================================================ # Any-objects If the type of a variable is not decided, you can use `any` as the type annotation, and this variable will be treated as a pure JavaScript object. ``` TypeScript let obj: any = { x: 1 }; ``` You can later assign other types to this variable, or add new fields to it. ``` TypeScript obj = 1; obj = "string"; obj = true; obj = { x: 1, y: 2 }; obj.z = 3; ``` > Note: any-objects works with low performance, it is always recommended to use statically typed variables whenever possible. ## boundary between static and dynamic world Since any-objects are managed by external environment, there is a boundary between static and dynamic world. Assigning a static variable to an `any-object` requires a boxing operation, while the reverse operation necessitates an unboxing process. ### box to any ``` TypeScript let static_number = 123; let static_object = { x: 1 }; let any_number: any = static_number; // box by value let any_object: any = static_object; // box by ref ``` box to any follow these rules: 1. primitive types (`number`, `boolean`, `string`) are boxed by value, the value of these variables are copied to external world, and further modification on these boxed any-objects will not affect the original value. 2. other types are boxed by reference. The external world will hold a reference to the original object, and modification on these boxed any-objects will be visible to the static world if the operated field exists in static world. ``` TypeScript let static_object = { x: 1 }; let any_object: any = static_object; // box by ref any_object.x = 2; // modify property console.log(static_object.x); // 2 console.log(any_object.x); // 2 ``` ### mixed type if new properties are added to these boxed any-objects, the newly added properties are managed by the external world, and not visible to the static world, these any-objects are called `mixed type` because they have both static and dynamic part. ``` TypeScript let static_object = { x: 1 }; let any_object: any = static_object; // box by ref any_object.y = 2; // add new property console.log(static_object.y); // compile error console.log(any_object.y); // 2 ``` ### unbox from any bring any-objects back to static world is called unboxing, in TypeScript this kind of operation will cause type unsoundness because the compiler will not apply type checking for any-objects involved assignment. In ts2wasm-compiler, additional instructions will be generated to do runtime type checking for these operations, and throw runtime error if the type is not matched. ``` TypeScript class A { x: number = 1; } class B { x: number = 1; y = 1; } let any_number: any = 123; let any_string : any = 'Hello'; let any_object: any = { x: 1 }; let any_object2: any = new A(); let static_number: number = any_number; // OK static_number = any_string; // runtime error let static_object: A = any_object; // runtime error static_object = any_object2; // OK let static_object2: B = any_object2; // runtime error ``` unbox any follow these rules: 1. primitive types (`number`, `boolean`, `string`) can be unboxed as long as their actual types are matched with the static ones. 2. other types can only be unboxed if they are previously boxed from the same static type, it is not possible to unbox a dynamic object to static world. ## Any-objects involved operations ts2wasm-compiler doesn't define operation between any-objects, when any-objects are involved in operations, the compiler will auto unbox them to static primitive values, and then perform the operation in static world. ![](./img/any_involved_operation.excalidraw.png) ``` TypeScript let any_number: any = 123; let any_string : any = 'Hello'; let any_object: any = { x: 1 }; let any_object2: any = new A(); let static_number: number = any_number + 1; // OK, any_number is unboxed to number static_number = any_number + any_number; // OK, any_number is unboxed to number static_number = any_string + 1; // runtime error, unbox any_string to number failed let static_object: A = any_object + any_object2; // runtime error, any_object and any_object2 are not primitive types ``` ================================================ FILE: doc/developer-guide/basic_concepts.md ================================================ # Basic concepts ## Types in TypeScript In TypeScript, every variable is associated with a type annotation, these annotations can be divided into two categories: 1. `typed` This kind of annotation contains the type information for the corresponding variables, it can be subdivided into the following categories: 1. `fully typed` Provide sufficient type information for static compilation, the memory layout can be decided by the compiler. E.g. `number`, `string` and `class` are treated as this category. 2. `incompletely typed` Provide limited information about the type, but these information is not enough for defining the memory layout at compile time. E.g. `interface` defines least required fields of the type, but the field layout is not defined; `union` defines the possible types of a variable, but we can't know what it will be during compilation. 2. `any` `any` is a pure JavaScript dynamic object, compiler have no information about `any-objects` (any-typed objects) ![](./img/type_category.excalidraw.png) As shown in the graph above, the fundamental processing principles for different type categories in ts2wasm-compiler following these rules: - `fully typed`: statically compiled and represented as Wasm value types or WasmGC types - `any`: represented as `externref`, and any operation on the `any-objects` will be compiled as invoking pre-defined APIs, which means the dynamic objects are managed by external environment. - `incompletely typed`: each specific type requires individual analysis based on the particular circumstances, e.g.: - `union` is actually a dynamic type because the type can change during runtime, so it's treated as `any` - `user defined interface` is widely used in TypeScript, treating it as dynamic will largely influence the performance, so we introduced `meta` to apply static compilation - `builtin objects` are objects provided by JavaScript runtime, TypeScript defines them as interface for type checking purpose. - Implementing these built-in objects demands a significant amount of effort, so we treat them as `any` by default, this allows us to immediately use the standard library implementation already available in external environment. (see [fallback](./fallback.md)) - Simultaneously, we are working on static compilation solutions for selected built-in objects (e.g. `String`, `Array`) to improve performance. The priority of static compilation for these objects is determined based on their actual usage frequency. ## Type system overview Ts2wasm compiler treats TypeScript as a mixed typed language, there are static types such as `Class`, `Primitives`, as well as dynamic types such as `any` and `union`, the developers should be aware that different types will have different performance impact, it is always recommended to reduce the usage of dynamic types. | ts type | category | wasm type | access strategy | performance overhead | | :----: | :----: | :----: | :----: | :----: | | boolean | fully typed | i32 | static | low | | number | fully typed |f64 | static | low | | string | fully typed |struct / stringref | static | low | | class | fully typed |struct | static | low | | function | fully typed |func | static | low | | interface | incompletely typed |struct | static + reflection | medium | | union | incompletely typed |externref | dynamic | high | | any | any |externref | dynamic | high | ================================================ FILE: doc/developer-guide/class.md ================================================ # Class Classes are declared using the `class` keyword ## Class fields ``` TypeScript class Car { price: number = 0; // field with initializer color: string; // field without initializer, must be initialized in constructor private owner: string; // field with visibility modifier readonly producer: string; // field with readonly modifier static isCar: boolean = true; // static field isNew? : boolean; // optional field method: (x: number) => number; // field with function type constructor(color: string) { // constructor this.color = color; this.owner = "B"; this.producer = "C"; this.isNew = true; this.method = (x: number) => x + 1; } } ``` ## Class method ``` TypeScript class Car { private speed_: number = 0; mileage = 0; // method with visibility modifier public drive(second: number) { this.mileage += this.speed_ * second; } // static method static isCar() { return true; } // getter get speed() { return this.speed_; } // setter set speed(sp: number){ this.speed_ = sp; } } ``` ## Inheritance ``` TypeScript class Car { price: number = 0; color: string; constructor(color: string) { this.color = color; } drive() { console.log("drive"); } } class Bus extends Car { height: number; constructor(color: string, height: number) { super(color); this.height = height; } drive() { console.log("bus drive"); super.drive(); } } ``` ## Instantiate class ``` TypeScript let car = new Car("red"); ``` derived class can be assigned to base class ``` TypeScript let bus: Car = new Bus("blue", 10); ``` ## Capture `this` Functions defined inside non-static method can capture `this` ``` TypeScript class A { n: number = 10; say(){ let anonymousFunc = () => console.log(this.n); return anonymousFunc; } } let a = new A(); a.say()(); // 10 ``` ## Limitations - ##### declare field in constructor parameter list is **not supported** ``` TypeScript class Car { // Not Supported constructor(private owner: string) { // field in constructor parameter list } } ``` - ##### Assign a function to a class method is **not supported** ``` TypeScript let car = new Car(); // Not Supported car.drive = (second: number) => { } ``` - ##### Class method with `this` parameter is **not supported** ``` TypeScript class Car { // Not Supported drive(this: Car, second: number) { } } ``` - ##### Use class as value is **not supported** ``` TypeScript // Not Supported let car = Car; ``` - ##### Index signatures for class is **not supported** ``` TypeScript class MyClass { // Not Supported [s: string]: boolean | ((s: string) => boolean); check(s: string) { return this[s] as boolean; } } ``` - ##### Assign to different class with same structure is **not supported by design** In ts2wasm-compiler, classes are treated as **nominal typing**. This is because class names represent abstractions of real-world entities, and defining class relationships based on structure rather than names can introduce error-prone code. ``` TypeScript class Person { name: string = ''; age: number = 0; } class Employee { name: string = ''; age: number = 0; salary: number = 0; } class Employee1 extends Person { salary: number = 0; } // Not Supported const p: Person = new Employee(); // OK const p1: Person = new Employee1(); ``` ================================================ FILE: doc/developer-guide/expose_host_API.md ================================================ # ts2wasm-compiler host API mechanism ts2wasm-compiler allows developer to expose their own native APIs to the application. ## Host implemented functions The `declare` keyword in TypeScript is used to declare a host function. When a function is marked as `declare`, ts2wasm-compiler will generate a wasm import entry into the final wasm module ``` TypeScript declare function native_func(x: number, msg: string): number; ``` This will generate: ``` wat (import "env" "native_func" (func $test|native_func-declare (type $f64_ref?|$string_type|_=>_f64) (param f64 (ref null $string_type)) (result f64))) ``` In the native world, the developer should implement the native API like this: ``` C double native_func_wrapper(wasm_exec_env_t exec_env, double x, wasm_struct_obj_t msg) { /* ... */ } ``` And then register this API to runtime: ``` C REG_NATIVE_FUNC(native_func, "(Fr)F"), ``` > Please refer to [WAMR export_native_api guide](https://github.com/bytecodealliance/wasm-micro-runtime/blob/main/doc/export_native_api.md) for more details. > Please refer to [WAMR GC API](https://github.com/bytecodealliance/wasm-micro-runtime/blob/dev/gc_refactor/core/iwasm/include/gc_export.h) to learn how to access the GC objects from native. ## Host implemented class It's also possible to declare a whole class to be host implemented: ``` TypeScript declare class DeclaredClass { grade: number; constructor(grade: number); sayHello(): void; static whoSayHi(name: string): number; get value(): any; set value(v: number); } ``` This will generate: ``` wat (import "env" "DeclaredClass_constructor" (func $test|DeclaredClass|constructor-declare (type $ref?|{}|_f64_=>_ref?|$cls-struct5|) (param (ref null ${}) f64) (result (ref null $cls-struct5)))) (import "env" "DeclaredClass_sayHello" (func $test|DeclaredClass|sayHello-declare (type $function0) (param (ref null ${})))) (import "env" "DeclaredClass_@whoSayHi" (func $test|DeclaredClass|@whoSayHi-declare (type $ref?|$string_type|_=>_f64) (param (ref null $string_type)) (result f64))) (import "env" "DeclaredClass_get_value" (func $test|DeclaredClass|get_value-declare (type $ref?|{}|_=>_anyref) (param (ref null ${})) (result anyref))) (import "env" "DeclaredClass_set_value" (func $test|DeclaredClass|set_value-declare (type $ref?|{}|_anyref_=>_none) (param (ref null ${}) anyref))) ``` The native implementation is similar to function, but remember that instance methods' first parameter should be `this` which pointing to the class instance. > Refer to [lib_console](../../runtime-library/stdlib/lib_console.c) as an example. ### Destructor When creating a class instance provided by host, the lifecycle should also be managed by native. WAMR provide a `wasm_obj_set_gc_finalizer` for setting a custom finalizer function on a certain object, so the developer can set a finalizer in the constructor API, then the native resource can be freed once the corresponding wasm object is claimed. ## Naming convention It is important to use a correct name when registering host APIs, assume the function name in ts source code is denoted as `$func`, and class name is denoted as `$cls`, then the host API should follow this naming convention: | category | import name | | :----: | :----: | | function | `$func` | | class constructor | `$cls`_constructor | | instance method | `$cls`_`$func` | | static method | `$cls`_@`$func` | | getter | `$cls`\_get_`$func` | | setter | `$cls`\_set_`$func` | > Currently the import module name is always `env`, customizable module name is not supported yet because there are no existing TypeScript syntax to describe this. We may introduce some configuration entries to support this later. ================================================ FILE: doc/developer-guide/fallback.md ================================================ # ts2wasm-compiler fallback mechanism The strategy of ts2wasm-compiler is to apply static compilation as much as possible, but there are circumstances where statization is not available, including: 1. The effort to implement static compilation for some object is relatively large, which isn't on high priority (e.g. `Map`, `Set`). 2. Accessing efficiency of some object is not critical to the application's overall performance (e.g. `Date`, `JSON`). In this case, ts2wasm-compiler supports a `fallback` mechanism - for scenario 1, this serves as a temporary workaround before the statical support is ready. - for scenario 2, this will be a formal solution to bridge common used JavaScript builtin method to TypeScript application. ## Example ``` TypeScript export function JSONTest() { let json = '{"result":true, "count":42}'; // The JSON.parse and JSON.stringify are implemented by JS runtime let obj = JSON.parse(json); let str: string = JSON.stringify(obj) as string; console.log(obj.count); console.log(str); } ``` ## Fallback whitelist - JSON object and all methods - Map constructor and all methods - Set constructor and all methods - Date constructor and all methods - Promise constructor, object and all methods > Note: The list of objects allowed to be fallbacked is restricted by the compiler to reduce test pressure, please contact the developer team if you need more fallback objects ================================================ FILE: doc/developer-guide/feature_list.md ================================================ # Wasmnizer-ts feature list Table column definition: - `feature` TypeScript keyword/type/syntax/concepts - `WAMR` Whether this feature is supported on [WebAssembly Micro Runtime (WAMR)](https://github.com/bytecodealliance/wasm-micro-runtime) - `chrome` Whether this feature is supported on chrome browser - `popularity` The frequency of the feature used in TypeScript. - empty means less used, or not evaluated - :star: means frequently used by experienced TypeScript developers - :star::star: means frequently used by both experienced and begging TypeScript developers ## Primitives | feature | WAMR | chrome | popularity | | :---: | :---: | :---: | :---: | | boolean | :heavy_check_mark: | :heavy_check_mark: | :star::star: | | number | :heavy_check_mark: | :heavy_check_mark: | :star::star: | | string | :heavy_check_mark: | :heavy_check_mark: | :star::star: | | string template | :heavy_check_mark: | :heavy_check_mark: | :star::star: | | bigint | :x: | :x: | | | symbol | :x: | :x: | | ## [Class](./class.md) | feature | WAMR | chrome | popularity | | :---: | :---: | :---: | :---: | | declaration | :heavy_check_mark: | :heavy_check_mark: | :star::star: | | inherit | :heavy_check_mark: | :heavy_check_mark: | :star::star: | | method overwrite | :heavy_check_mark: | :heavy_check_mark: | :star::star: | | static field/method | :heavy_check_mark: | :heavy_check_mark: | :star::star: | | field initializer | :heavy_check_mark: | :heavy_check_mark: | :star::star: | | visibility control | :heavy_check_mark: | :heavy_check_mark: | :star::star: | | getter/setter | :heavy_check_mark: | :heavy_check_mark: | | [class as value](./class.md#use-class-as-value-is-not-supported) | :x: | :x: | ## [Function](./function.md) | feature | WAMR | chrome | popularity | | :---: | :---: | :---: | :---: | | closure | :heavy_check_mark: | :heavy_check_mark: | :star::star: | | optional parameter | :heavy_check_mark: | :heavy_check_mark: | :star::star: | | function default parameter | :heavy_check_mark: | :heavy_check_mark: | :star::star: | | method default parameter | :heavy_check_mark: | :heavy_check_mark: | :star::star: | | closure default parameter | :x: | :x: | | | destructor parameter | :x: | :x: | :star::star: | | rest parameter | :heavy_check_mark: | :heavy_check_mark: | :star: | | this binding | :x: | :x: | :star: | | overload | :x: | :x: | :star: | ## [Interface](./interface.md) | feature | WAMR | chrome | popularity | | :---: | :---: | :---: | :---: | | explicitly implemented interface | :heavy_check_mark: | :heavy_check_mark: | :star::star: | | implicitly implemented interface | :heavy_check_mark: | :x: | :star::star: | | readonly fields | :heavy_check_mark: | :x: | | | function signature | :x: | :x: | | | indexed signature | :x: | :x: | | ## Enum | feature | WAMR | chrome | popularity | | :---: | :---: | :---: | :---: | | numeric enum | :heavy_check_mark: | :heavy_check_mark: | :star::star: | | string enum | :heavy_check_mark: | :heavy_check_mark: | :star::star: | | heterogeneous enum | :x: | :x: | | ## [Built-in objects/method](../standard-library/index.md) | feature | WAMR | chrome | popularity | note | | :---: | :---: | :---: | :---: | :---: | | [console](../standard-library/console.md) | :heavy_check_mark: | :x: | :star::star: | only support `log` | | Object | :x: | :x: | :star: | | | Function | :x: | :x: | | | | JSON | :heavy_check_mark: | :x: | :star::star: | [fallback to dynamic](./fallback.md) | | Date | :heavy_check_mark: | :x: | :star::star: | [fallback to dynamic](./fallback.md) | | [Math](../standard-library/math.md) | :heavy_check_mark: | :heavy_check_mark: | :star::star: | only support `pow`, `max`, `min`, `sqrt`, `abs`, `ceil`, `floor` | | Number | :x: | :x: | :star::star: | | | [String](../standard-library/string.md) | :heavy_check_mark: | :heavy_check_mark: | :star::star: | | | [Array](../standard-library/array.md) | :heavy_check_mark: | :x: | :star::star: | | | Map | :heavy_check_mark: | :x: | :star::star: | [fallback to dynamic](./fallback.md) | | Set | :heavy_check_mark: | :x: | :star: | [fallback to dynamic](./fallback.md) | | ArrayBuffer | :x: | :x: | :star: | | | RegExp | :x: | :x: | :star: | | | ... others | :x: | :x: | | | ## Wasm runtime capabilities | feature | WAMR | chrome | popularity | note | | :---: | :---: | :---: | :---: | :---: | | exception handling | :x: | :heavy_check_mark: | :star::star: | | | promise | :heavy_check_mark: | :x: | :star::star: | [fallback to dynamic](./fallback.md) | | source debugging | :x: | :heavy_check_mark: | :star::star: | | | AoT compilation | :x: | :x: | | | | async/await | :x: | :x: | :star::star: | | | import host API | :heavy_check_mark: | :heavy_check_mark: | | [import host API](./expose_host_API.md) | ## [Dynamics](./any_object.md) | feature | WAMR | chrome | popularity | note | | :---: | :---: | :---: | :---: | :---: | | any | :heavy_check_mark: | :heavy_check_mark: | :star: | | | unknown | :x: | :x: | | | | never | :x: | :x: | | | | assign static to any | :heavy_check_mark: | :heavy_check_mark: | | | | assign any to static | :heavy_check_mark: | :heavy_check_mark: | | | | property access | :heavy_check_mark: | :heavy_check_mark: | :star::star: | | | prototype | :heavy_check_mark: | :heavy_check_mark: | :star: | | | comparison | :heavy_check_mark: | :x: | :star: | | | arithmetic operation | :heavy_check_mark: | :heavy_check_mark: | :star::star: | only support `number` and `string` | | mixed type | :heavy_check_mark: | :x: | | Box static object to any and add new property on it | | dynamic function | :x: | :x: | | | | eval | :x: | :x: | | | ## Type casting | feature | WAMR | chrome | popularity | note | | :---: | :---: | :---: | :---: | :---: | | static to static | :heavy_check_mark: | :heavy_check_mark: | | static type checking | | static to dynamic | :heavy_check_mark: | :heavy_check_mark: | |always success | | dynamic to static | :heavy_check_mark: | :heavy_check_mark: | |runtime type checking | | dynamic to dynamic | :heavy_check_mark: | :heavy_check_mark: | | no check | ## Misc | feature | WAMR | chrome | popularity | note | | :---: | :---: | :---: | :---: | :---: | | typeof | :heavy_check_mark: | :x: | :star: | | | instanceof | :heavy_check_mark: | :heavy_check_mark: | :star: | | | toString | :heavy_check_mark: | :x: | :star::star: | | | for ... of | :heavy_check_mark: | :heavy_check_mark: | :star::star: | | | for ... in | :x: | :x: | :star::star: | | | generic | :x: | :x: | | | | module (static import) | :heavy_check_mark: | :heavy_check_mark: | :star::star: | | | module (dynamic import) | :x: | :x: | | | ================================================ FILE: doc/developer-guide/function.md ================================================ # Function and closure ## Declaration There are three kind of syntax to define a function: 1. Function declaration syntax ``` TypeScript function add(x: number, y: number): number { return x + y; } ``` - if this is in global scope - This defines a function with name `add`, and it can be directly called through the name `add`. - if this is inside another function scope - This defines a function with a mangled name which can not be directly invoked, the compiler will implicitly create a closure with the given name `add` in the outer scope, 2. Function expression syntax ``` TypeScript let add = function(x: number, y: number): number { return x + y; } ``` > This defines an anonymous function, and assigned to variable `add` as a `closure` 3. Arrow function syntax ``` TypeScript let add = (x: number, y: number): number => { return x + y; } let add1 = (x: number, y: number): number => x + y; ``` > This defines two anonymous functions, and assigned to variable `add` and `add1` as `closure` ## Parameters ### Optional parameter ``` TypeScript function add(x: number, y: number, z?: number): number { if (z) { // type narrowing not supported, currently must explicitly cast the type let z_value: number = z; return x + y + z_value; } else { return x + y; } } ``` ### Default parameter ``` TypeScript function add(x: number, y: number, z: number = 0): number { return x + y + z; } ``` > Note: Default parameter is not supported in `class static method` and `closure` ### Rest parameter ``` TypeScript function add(x: number, y: number, ...z: number[]): number { let sum = x + y; for (let i = 0; i < z.length; i++) { sum += z[i]; } return sum; } ``` ## Function type and type alias ``` TypeScript type Add = (x: number, y: number) => number; let add: (x: number, y: number) => number = function(x: number, y: number): number { return x + y; } let add1: Add = function(x: number, y: number): number { return x + y; } ``` # Limitations - Function overload is **not supported** ``` TypeScript function add(x: number, y: number): number; function add(x: string, y: string): string; function add(x: any, y: any): any { return x + y; } ``` ================================================ FILE: doc/developer-guide/generic.md ================================================ # Generic Type Process ## Generic Types At present, the processing of generic types mainly focuses on generic functions and generic classes. ### generic function ``` function genericFunc(param: T): T { return param; } export function numberFunc() { console.log(genericFunc(100)); } ``` ### generic class ``` class GenericClass { x: X; constructor(x: X) { this.x = x; } echo(param: X) { return param; } } const a = new GenericClass('string'); console.log(a.echo('hello world')); ``` ## Type Process This compiler will perform multiple scans during the `syntax` processing stage, the processing of types runs through these scans. After the `syntax` processing stage, basic types (such as ***number***, ***string***) will be passed directly to the next processing stage; complex types (such as ***class***, ***function***) will generate `Scope`s (such as ***ClassScope***, ***FunctionScope***) for processing in the `semantic` processing stage. The creation of each `Scope` relies on the corresponding ***DeclarationNode*** in AST tree. Take the following case as an example to explain how the compiler processes generic types. - - - ``` // example.ts function genericFunc(param: T): T { return param; } class GenericClass { x: X; constructor(x: X) { this.x = x; } echo(param: X) { return param; } } const ret = genericFunc(100); console.log(ret); const a = new GenericClass('string'); const echoWord = a.echo('hello world'); console.log(echoWord); ``` - - - ### Scope Scan Starting from the root node of the syntax tree, traverse the entire AST: - when a ***SourceFile*** node is scanned, a `GlobalScope` is created - when a ***ClassDeclaration*** node is scanned, a `ClassScope` is created - when a ***FunctionDeclaration*** node is scanned, a `FunctionScope` is created After this scan, the compiler will create a scope tree with `GlobalScope` as the root for each ts source file to organize the file content.
- - - ### Type Scan In this scanning phase, the compiler mainly processes the following nodes in the AST: - **VariableDeclarationNode**: create a type based on the initialization expression of the variable - **Parameter**: create a type based on the type information of the parameter - **ClassDeclarationNode**: create a `TSClass` type - **InterfaceDeclarationNode**: create a `TSInterface` type - **FunctionDeclarationNode**: create a `TSFunction` type After creating a new type, the type information will be stored in parent scope's ***namedTypeMap***.
- - - ### Variable scan In the variable scan phase, the compiler will analyze and process the ***VariableDeclarationNode*** and ***Parameter*** starting from the root of the AST. At this time, the specialization of generic types may be performed. - - - #### Specialization of Generic Function When processing the global variable `const ret = genericFunc(100);`, the specialization of the generic function will be performed. ``` The time for specializing a generic function is when the function is called. ``` Specialization of generic functions mainly performs the following two operations: - generate a new `TSFunction` type based on the type arguments and the generic `TSFunction` type - generate a new `FunctionScope` based on the type arguments and the generic `FunctionScope`
The parameters and local variables in the new `FunctionScope` will not be processed at this time. We will process them uniformly when processing statements. - - - #### Specialization of Generic Class When processing the global variable `const a = new GenericClass('string');`, the specialization of the generic class will be performed. ``` The time for specializing a generic class is when creating an class instance. ``` Specialization of generic classes mainly performs the following two operations: - generate a new `TSClass` type based on the type arguments and the generic `TSClass` type - generate a new `ClassScope` based on the type arguments and the generic `ClassScope`
- - - ### Statement Scan In this scanning phase, the compiler generates the statements in `FunctionScope` based on the content of **FunctionDeclarationNode**.
- - - Since the specialized function does not have a corresponding **FunctionDeclarationNode** on the AST tree, the generation of its statements relies on the generic `FunctionScope`: - generate a specialized **ParameterArray** based on **ParameterArray** and type arguments of generic `FunctionScope` - generate a specialized **VariableArray**(such as ***context***, ***this***) based on **VariableArray** and type arguments of generic `FunctionScope` - generate new statements based on the **StatementArray** and type arguments of the generic `FunctionScope`. When processing a VariableStatement, after specializing this statement, add the local variable represented by the statement to **VariableArray**.
- - - ### specialized type generation After these scanning phases, the specialization of a generic type is completed. ================================================ FILE: doc/developer-guide/import_export.md ================================================ # import and export In ts2wasm-compiler, every file can import/export element from/to other files. The dependencies are resolved at compile time, any missing dependency will cause a compile error. If no error occurs during dependency resolving, the compiler will generate a **single wasm module** for all the files. Dynamic import is not possible yet. ## export function ``` TypeScript export function add(x: number, y: number): number { return x + y; } ``` ## export class ``` TypeScript export class A { x: number = 1; y: number = 2; } ``` ## export variable ``` TypeScript export let x: number = 1; ``` ## default export ``` TypeScript export default function add(x: number, y: number): number { return x + y; } ``` ## export alias ``` TypeScript export { add as add1, A, x }; ``` ## re-export ``` TypeScript export { add as add1, A, x } from "./export"; ``` > Note: re-export all is not supported ## import ``` TypeScript import { add, A, x } from "./export"; ``` ## import all ``` TypeScript import * as exp from "./export"; ``` ## import alias ``` TypeScript import { add as add1, A, x } from "./export"; ``` ## import default ``` TypeScript import def from "./export"; ``` ================================================ FILE: doc/developer-guide/index.md ================================================ # Wasmnizer-ts developer guide TypeScript is a typed superset of JavaScript, its rich type information has been employed by numerous tools to facilitate tasks such as refactoring and linting. However, TypeScript code must be transpiled into pure JavaScript before execution, resulting in the loss of all type information. The ts2wasm compiler works like a backend to the TypeScript Compiler (tsc), it utilize the power of WebAssembly Garbage Collection proposal (WasmGC) to perform static compilation wherever possible. It also provides some escape hatches to accommodate dynamic types. The ts2wasm-compiler now supports a strict subset of TypeScript and continuously strives to accommodate more semantics. This document serves as an overview of the supported language features and highlights certain known limitations. ## Suggestions for reading 1. read [basic concepts](./basic_concepts.md) to understand the fundamental design principal of ts2wasm. 2. go through [feature list](./feature_list.md) to understand the supported language features. 3. jump to the detail through the link of specific feature in [feature list](./feature_list.md) if you are interested. ## Supported features Please refer to [feature list](./feature_list.md) It's hard to enumerate every detailed syntax in the list, please refer to our [test cases](../../tests/samples/) for more samples. ================================================ FILE: doc/developer-guide/interface.md ================================================ # interface ## Interface declaration ``` TypeScript interface I { x: number; readonly z: string; // field with readonly modifier foo: () => number; // method set m(v: number); // setter get m(); // getter } ``` ## Implement interface ``` TypeScript class C implements I { x: number = 0; z: string = "z"; get m() { return this.x; } set m(v: number) { this.x = v; } foo(): number { return 0; } } ``` In TypeScript, `implements` is not required, any objects satisfies the fields defined by the interface are treated implemented those interfaces. But it is recommended to use `implements` when using ts2wasm-compiler since the compiler may apply further optimization according to these information. ## Assign to interface interface can be assigned from an object literal or class instance. ``` TypeScript interface I { x: number; } let obj: I = { x: 10, } class A { y: number = 2; x: number = 1; } obj = new A(); ``` ## Optional fields ``` TypeScript interface I { x: number; y?: number; } let i : I = { x: 1 }; ``` Reading an un-exist optional field will return `undefined`. Writing an un-exist optional field will cause an error since it's not possible to add a new field to an static object. ``` TypeScript console.log(i.y); // undefined i.y = 1; // runtime error ``` # Limitations - interface extends is **not supported** ``` TypeScript interface I1 { x: number; } // Not Supported interface I2 extends I1 { y: number; } ``` - indexed signature interface is **not supported** ``` TypeScript interface I { [index: number]: number; } ``` - function signature interface is **not supported** ``` TypeScript interface I { (x: number): number; } ``` ================================================ FILE: doc/developer-guide/wasmType_usage.md ================================================ # Use wasmType in typescript ## wasmType declaration Now we support use wasmType directly in typescript, these below types are supported: ### wasm basic type - `i32` - `i64` - `f32` - `f64` - `anyref` ### wasm heap type - `array` - `struct` ## wasmType usage During usage, we must follow some rules. And the wasm basic type rules is differ from wasm heap type rules. ### wasm basic type We can use the wasm basic type as ts type name directly. ### wasm heap type We should set a special comment to indicate that a wasm heap type structure will be created. 1. For `array`, we use `comment + array type alias` to represent the raw wasm array type. ```ts // Wasmnizer-ts: @WASMArray@ type arrayType1 = string[]; ---> will create a raw wasm array type: array // Wasmnizer-ts: @WASMArray@ type arrayType2 = i32[]; ---> will create a raw wasm array type: array ``` **Hint: `// Wasmnizer-ts: @WASMArray@ ` is necessary, and `` is optional. The latter shows that `if the array element is packed`, `if the array element is mutable`, `if the array is nullable`. The default value is `Not_Packed`, `Mutable` and `Nullable`.** 2. For `struct`, we use `comment + tuple type alias` to represent the raw wasm struct type. ```ts // Wasmnizer-ts: @WASMStruct@ <[Not_Packed, Not_Packed], [Mutable, Mutable], Nullable, NULL> type structType1 = [arrayType1, i64]; ---> will create a raw wasm struct type: struct[array, i64] // Wasmnizer-ts: @WASMStruct@ type structType2 = [i64, i32]; ---> will create a raw wasm struct type: struct[i64, i32] ``` **Hint: `// Wasmnizer-ts: @WASMStruct@ ` is necessary, and `<[Not_Packed, ...], [Mutable, ...], Nullable, BaseTypeName>` is optional. The latter shows that `if the struct fields are packed`, `if the struct fields are mutable`, `if the struct is nullable`, `the struct's base type name`. The default value is `[Not_Packed, ...]`, `[Mutable, ...]`, `Nullable` and `NULL`.** The comments' optional attributes can be one of these enum value: ```ts export enum PackedTypeKind { Not_Packed = 'Not_Packed', I8 = 'I8', I16 = 'I16', } export enum MutabilityKind { Immutable = 'Immutable', Mutable = 'Mutable', } export enum NullabilityKind { NonNullable = 'NonNullable', Nullable = 'Nullable', } ``` ## Example ### Used as basic type If we define the wasmtype for variables, and the right value is LiteralValue or variables with the same wasmtype, the **no cast** will be generated. ```ts const a: i32 = 100; --> (i32.const 100) ``` ```ts const a: i64 = 100; --> (i64.const 100) ``` ```ts const a: f32 = 100; --> (f32.const 100) ``` ```ts const a: f64 = 100; --> (f64.const 100) ``` ```ts // Wasmnizer-ts: @WASMArray@ type arrayType2 = i32[]; const a: arrayType2 = [100]; --> (array.new_fixed $array0 1 (i32.const 100) ) ``` ```ts // Wasmnizer-ts: @WASMStruct@ type structType2 = [i64, i32]; const a: structType2 = [100, 200] ---> (struct.new $45 (i64.const 100) (i32.const 200) ) ``` If we don't define the wasmtype explicitly, then the variable will be regard as `number` type, **one cast** will be occurs. ```ts const a = 100 as i32; ---> (f64.convert_i32_s (i32.const 100) ) ``` ### Used as array element type The array type should be explicitly specified too. ```ts const a: i32[] = [1, 2]; --> a will be regarded as i32[], the elements in right value are both i32. since we use struct to represent ts array, so the wasm structure is struct[array, i32]. ``` ```ts const x: arrayType2 = [100]; const y: arrayType2 = [200]; const a: arrayType2[] = [x, y]; --> a will be regarded as arrayType2[], the elements in right value are both arrayType2. since we use struct to represent ts array, so the wasm structure is struct[array>, i32]. ``` If array's type is not explicitly specified, then left value is regarded as number[], compilation error will occur. ```ts let a1: i32 = 1; let a2: i32 = 2; let a = [a1, a2]; --> a will be regarded as number[], compile will fail. ``` ### Used as class property type Each property's wasm type should be explicitly specified. ```ts class A { a: i32 = 1; b: i64 = 2; c: f32 = 3; d: f64 = 4; e: arrayType2 = [5]; } --> The properties type are i32, i64, f32, f64, array type. ``` If property's type is not explicitly specified, they will be regarded as original ts type, and **one cast** will occur. ```ts class A { a = 1 as i32; b = 2 as i64; c = 3 as f32; d = 4 as f64; } --> The properties type are both number type, and a, b, c all will be cast to f64. ``` Wasm heap type can not be used as casted target since the ts original type `number[]` can not be casted to `WASMArrayType`: ```ts class A { e = [5] as arrayType2 } --> Will cause compilation error since `cannot make cast value from "Array(-1)" to "WASM_ARRAY(58)"` ``` ### Used as interface property type Each property's wasm type should be explicitly specified. ```ts interface I { a: i32; b: i64; c: f32; d: f64; e: arrayType2; } --> The properties type are i32, i64, f32, f64, array type. ``` ### Used as object literal property type Since object literal's properties' type can not be defined, we only provide its value, so we judge properties' type by its real value type. ```ts const x: arrayType2 = [5]; const obj = { a: 1 as i32, b: 2 as i64, c: 3 as f32, d: 4 as f64, e: x as arrayType2, } --> The properties type are i32, i64, f32, f64, array type. ``` So, if we assign the obj's type to an interface type which has wasmtype, then we should ensure that the properties' value type should be wasmtype too. ```ts interface I { a: i32; b: i64; c: f32; d: f64; e: arrayType2; } const x: arrayType2 = [5]; const obj: I = { a: 1 as i32, b: 2 as i64, c: 3 as f32, d: 4 as f64, e: x as arrayType2, } ---> compile success ``` ```ts interface I { a: i32; b: i64; c: f32; d: f64; e: arrayType2; } const obj: I = { a: 1, b: 2, c: 3, d: 4, e: [5], } ---> compile fail ``` ### Used as funtion param type & return type The parameter's type and return type should be explicitly specified when using wasmtype. ```ts function test(): i32 { return 100 as i32; } --> The return type is i32 ``` ```ts function test() { return 100 as i32; } --> One cast will occur, the return type is number. (return (f64.convert_i32_s (i32.const 100) ) ) ``` ```ts function test(): arrayType2 { const x: arrayType2 = [100]; return x; } --> The return type is array. ``` ### type casting in binary operations If two operators with wasm type operate binary operations, they will cast to the larger type, and operate. ```ts const a: i32 = 100; const b: f32 = 80.75; const c = a + b; ---> (local.set $0 (i32.const 100) ) (local.set $1 (f32.const 80.75) ) (local.set $2 (f64.promote_f32 (f32.add (f32.convert_i32_s (local.get $0) ) (local.get $1) ) ) ) ``` ================================================ FILE: doc/environment_variables.md ================================================ # ts2wasm environment variables This document record useful environment variables used during development, these variables may or may not be included in final release. - **TS2WASM_DUMP_SCOPE** if not none, ts2wasm will dump the scope information before code generation - **TS2WASM_VALIDATE** if not none, ts2wasm will run binaryen's validation function to print any error found in the generated wasm module ================================================ FILE: doc/faq.md ================================================ # Frequently Asked Questions (FAQ) This document provides answers to some of the most frequently asked questions about Wasmnizer-ts. It is designed to help users understand the project better. Please note that this document is continually updated and improved. If your question is not addressed here, feel free to raise an issue or contribute to the project. ### Q: Is the end goal support for the full TypeScript language? What about JavaScript? We are trying to support more language features, but since WebAssembly opcode is statically compiled, a full coverage of TypeScript not possible. For example, `eval` and `function bind` are not supported. This may change as the Wasm specification evolves. As for JavaScript, it's not in the scope of Wasmnizer-ts. In Wasmnizer-ts we are trying to use the type information in source code to do static compilation. The more type information in the source code, the better performance for the generated wasm module. Since JavaScript contains no type information, then Wasmnizer-ts can't get any information for static compilation. For executing pure JavaScript in WebAssembly, maybe the VM-in-VM approach (build a JavaScript VM to WebAssembly) is more suitable. ### Q: What's an appropriate usecase for Wasmnizer-ts? Wasmnizer-ts brings a subset of TypeScript to WebAssembly, so it can be used in many areas as long as they need programming capabilities, some typical scenarios include: - Application programming language for IoT devices. Wasmnizer-ts leverages WasmGC, so there is need to compile a whole language runtime inside WebAssembly, making the generated wasm module very small; - Application programming language for function computing (FaaS) Wasmnizer-ts brings a new choice for developers; - A more friendly WebAssembly targeted language for frontend developers In frontend projects, it's very common to build some CPU intensive logic to WebAssembly to get better performance. Currently some statically typed languages such as C/C++/Rust can be successfully compiled to WebAssembly and work well, but the frontend developers may not be familiar with these languages. Wasmnizer-ts provides a new choice: the frontend developers can write TypeScript, building on their experience with JavaScript, then compile to WebAssembly. ### Q: What's the difference between Wasmnizer-ts and devicescript (https://github.com/microsoft/devicescript) ? DeviceScript is a very interesting project which we evaluated, as well as Static TypeScript, before we started Wasmnizer-ts. We got lots of ideas about which syntax to support thanks to these projects. We're really happy to see that DeviceScript has so many useful features and APIs added. There are some fundamental difference between Wasmnizer-ts and DeviceScript: from what we can tell, DeviceScript uses a self-defined bytecode, while Wasmnizer-ts uses WasmGC opcode. Currently WasmGC is in an early stage, so there are challenges for us to implement some features. However, as a standard bytecode format, we can gradually benefit from the new spec proposals (e.g. WasmGC Post-MVP, ESM-integration, etc), and we may even reuse some standard WASI APIs or even re-use more wasm ecosystem components in the future. In addition we contribute to WAMR, where we implemented the WasmGC proposal but we find that there isn't too many toolchains ready for WasmGC (at time of writing, Kotlin and Dart have experimental support), so this project is also an exploration to understand how WasmGC can be used in real languages so that we can optimize our runtime implementation and even propose more useful opcodes. We are determined and interested in driving the development of Wasmnizer-ts. It's great to see projects like DeviceScript which provide developer friendly experience in the embedded space. ================================================ FILE: doc/getting_started.md ================================================ # Getting Started ## Build the compiler 1. clone the repo ``` bash git clone https://github.com/intel/Wasmnizer-ts.git ``` 2. install the dependencies ``` bash cd Wasmnizer-ts npm install ``` 3. build ``` bash npm run build ``` ## Build ts source code ``` bash cd build node cli/ts2wasm.js --opt=3 -o out.wasm ``` ## Build wasm module to AoT module ``` bash # Build WAMR AoT compiler cd runtime-library/deps/wamr-gc/wamr-compiler ## Follow the instructions in the README.md to build wamrc (please add -DWAMR_BUILD_GC_BINARYEN=1 during cmake configuration) # Generate AoT module /path/to/wamrc --enable-gc -o out.aot out.wasm ``` Please refer to [developer guide](./developer-guide/index.md) for feature description. ## Execute the generated module - Execute on WAMR Refer to [iwasm_gc](../runtime-library/README.md) ## Source debugging (browser only) To debug the generated wasm module, use `--debug --sourceMap` command line options to generate wasm module containing source map. ```bash node cli/ts2wasm.js -o out.wasm --debug --sourceMap ``` ================================================ FILE: doc/libdyntype_api_spec.md ================================================ # libdyntype API spec ## Overview `libdyntype` is a library for supporting dynamic objects for WebAssembly. It provides APIs for creating, accessing and manipulating dynamic objects. The dynamic objects are represented as `externref` in WebAssembly, the actual memory space is created and managed by external environment. > Note: in current implementation, we use `anyref` rather than `externref` to avoid `internalize` and `externalize` operations, we will switch to `externref` once we confirm the overhead of `internalize/externalize` is acceptable. ![](./img/libdyntype_any_object.excalidraw.png) The dynamic objects managed by `libdyntype` are called `any-object`, it can be divided into two categories: 1. `pure dynamic type`: these objects are originally created from libdyntype. 2. `extref`: these objects are created when some static objects are assigned to an `any-object`. It's still a dynamic object, but it has some special properties to store references to the static object. ![](./img/libdyntype_type_category.excalidraw.png) For `extref`, there are two levels for the implementation: - level-1: - Support creating `extref` when assigning static object to any-object, but can't access the static fields through libdyntype APIs, the `extref` must be cast back to static type before accessing. > level-1 is not bound to a specific source language to be compiled to WebAssembly, libdyntype developer don't need to learn details of the language design and wasm runtime APIs. - level-2 (**mixed-type**): - Based on level 1, additional support for accessing static fields through libdyntype APIs. Also support add new dynamic properties to the `extref`. > level-2 is bound to a specific source language as well as the target wasm runtime, libdyntype developer must know: - the object layout of the specific languages - the wasm runtime APIs for accessing the fields of these static objects, or APIs for invoking wasm functions ## Dependent status These APIs are required by `dynamic types`, the absence of this set of APIs would prevent apps utilizing dynamic types from functioning correctly. > Note: some standard libraries may depends on libdyntype, e.g. `console.log`, so it is likely most apps will not work if libdyntype is not provided. ## Concepts - **dyntype context** An opaque pointer provided by the libdyntype implementer, this pointer will be stored in application once startup, and passed to all APIs as the first argument. If the libdyntype implementation doesn't need a context, it can be `NULL`. - **extref** In `ts2wasm-compiler` there are both static and dynamic types, if a statically-typed variable is assigned to an any-typed one, then the dynamic object actually hold a reference to the static world, this kind of object is called `extref`. > Note that the world `extref` here is different from the `externref` in WebAssembly, it is viewed from the libdyntype's perspective. The extref must record two things: 1. A tag to indicate whether the static object is an `object`, `array` or `closure` 2. A reference to the static object, the reference is a table element index inside wasm table The actual representation of `extref` is implementer-defined, it doesn't need to be a new type. A possible representation is a normal `object` with specific fields to store the tag and reference to static object. - **thread-safety** `libdyntype` assumes applications are executed in a single thread environment, no thread safety is guaranteed. - **WasmGC string** string represented by WasmGC array + WasmGC struct, the struct contains two fields: 1. `flag: i32`: a reserved field for encoding and other future use 2. `data: array`: the array to store string content ## Module name `libdyntype` ## APIs - **dyntype_get_context** - **Description** - Get the dyntype context - **Parameters** - None - **Return** - `externref`: the dyntype context - **dyntype_new_number** - **Description** - Create a dynamic typed number - **Parameters** - `externref`: the dyntype context - `f64`: the number value - **Return** - `externref`: the created number - **dyntype_new_boolean** - **Description** - Create a dynamic typed boolean - **Parameters** - `externref`: the dyntype context - `i32`: the boolean value - **Return** - `externref`: the created boolean - **dyntype_new_string** - **Description** - Create a dynamic typed string - **Parameters** - `externref`: the dyntype context - `structref`: the wasm represented string - **Return** - `externref`: the created string - **dyntype_new_undefined** - **Description** - Create a dynamic typed undefined - **Parameters** - `externref`: the dyntype context - **Return** - `externref`: the created undefined - **dyntype_new_null** - **Description** - Create a dynamic typed null - **Parameters** - `externref`: the dyntype context - **Return** - `externref`: the created null - **dyntype_new_object** - **Description** - Create a dynamic typed object - **Parameters** - `externref`: the dyntype context - **Return** - `externref`: the created object - **dyntype_new_object_with_proto** - **Description** - Create a dynamic typed object with prototype - **Parameters** - `externref`: the dyntype context - `externref`: the prototype of the object - **Return** - `externref`: the created object - **dyntype_new_object_with_class** - **Description** - Create a dynamic typed object with class - **Parameters** - `externref`: the dyntype context - `i32`: the class name (string) - `externref`: rest parameters array - **Return** - `externref`: the created object - **dyntype_new_array** - **Description** - Create a dynamic typed array with length - **Parameters** - `externref`: the dyntype context - `i32`: the length of the array - **Return** - `externref`: the created array - **dyntype_new_extref** - **Description** - Create a dynamic typed extref - **Parameters** - `externref`: the dyntype context - `i32`: the tag to indicate the static object is `object`|`array`|`closure` - `i32`: the reference to the static object (wasm table element index) - **Return** - `externref`: the created extref - **dyntype_set_elem** - **Description** - if it's a dynamic typed array: - Set an element to the dynamic typed array - if it's an extref: - Level 1: raise exception: `libdyntype: unsupport operation for extref: set_elem` - Level 2: set an element to the boxed static typed array - **Parameters** - `externref`: the dyntype context - `externref`: the array - `i32`: the index of the element to be set - `externref`: the element to be set - **Return** - None - **dyntype_get_elem** - **Description** - if it's a dynamic typed array: - Get an element from the dynamic typed array - if it's an extref: - Level 1: raise exception: `libdyntype: unsupport operation for extref: get_elem` - Level 2: get an element to the boxed static typed array - **Parameters** - `externref`: the dyntype context - `externref`: the array - `i32`: the index of the element to be get - **Return** - `externref`: the element - **dyntype_set_property** - **Description** - if it's a dynamic typed object: - Set the property of the dynamic typed object - if it's an extref: - Level 1: raise exception: `libdyntype: unsupport operation for extref: set_property` - Level 2: set the field value of the boxed static typed object if the field exists in the static object, otherwise add a dynamic property - **Parameters** - `externref`: the dyntype context - `externref`: the object - `i32`: the property name (string) - `externref`: the property value - **Return** - `i32`: result, 0 for success, -1 otherwise - **dyntype_get_property** - **Description** - if it's a dynamic typed object: - Get the property of the dynamic typed object - if it's an extref: - Level 1: raise exception: `libdyntype: unsupport operation for extref: get_property` - Level 2: get the field value of the boxed static typed object if the field exists in the static object, otherwise return `undefined` - **Parameters** - `externref`: the dyntype context - `externref`: the object - `i32`: the property name (string) - **Return** - `externref`: the property value - **dyntype_get_own_property** - **Description** - if it's a dynamic typed object: - Get the own property of the dynamic typed object - if it's an extref: - Level 1: raise exception: `libdyntype: unsupport operation for extref: get_own_property` - Level 2: get the field value of the boxed static typed object if the field exists in the static object, otherwise return `undefined` - **Parameters** - `externref`: the dyntype context - `externref`: the object - `i32`: the property name (string) - **Return** - `externref`: the property value - **dyntype_define_property** - **Description** - if it's a dynamic typed object: - Define the property of the dynamic typed object - if it's an extref: - raise exception: `libdyntype: unsupport operation for extref: define_property` - **Parameters** - `externref`: the dyntype context - `externref`: the object - `i32`: the property name (string) - `externref`: the property descriptor - **Return** - `i32`: result, 0 for success, -1 otherwise - **dyntype_has_property** - **Description** - if it's a dynamic typed object: - Check if the dynamic typed object has the property - if it's an extref: - Level 1: raise exception: `libdyntype: unsupport operation for extref: has_property` - Level 2: check if the field exists in the static typed object - **Parameters** - `externref`: the dyntype context - `externref`: the object - `i32`: the property name (string) - **Return** - `i32`: result, 1 if property exists, 0 otherwise - **dyntype_delete_property** - **Description** - if it's a dynamic typed object: - Delete the property of the dynamic typed object - if it's an extref: - Level 1: raise exception: `libdyntype: unsupport operation for extref: delete_property` - Level 2: if the field exists in the boxed static object, raise exception: `libdyntype: delete property on static type object`, otherwise delete the dynamic property of the mixed type - **Parameters** - `externref`: the dyntype context - `externref`: the object - `i32`: the property name (string) - **Return** - `i32`: result, 0 for success, -1 otherwise - **dyntype_is_number** - **Description** - Check if the dynamic typed value is a number - **Parameters** - `externref`: the dyntype context - `externref`: the value - **Return** - `i32`: result, 1 if it is a number, 0 otherwise - **dyntype_is_bool** - **Description** - Check if the dynamic typed value is a boolean - **Parameters** - `externref`: the dyntype context - `externref`: the value - **Return** - `i32`: result, 1 if it is a boolean, 0 otherwise - **dyntype_is_string** - **Description** - Check if the dynamic typed value is a string - **Parameters** - `externref`: the dyntype context - `externref`: the value - **Return** - `i32`: result, 1 if it is a string, 0 otherwise - **dyntype_is_undefined** - **Description** - Check if the dynamic typed value is an undefined - **Parameters** - `externref`: the dyntype context - `externref`: the value - **Return** - `i32`: result, 1 if it is an undefined, 0 otherwise - **dyntype_is_null** - **Description** - Check if the dynamic typed value is a null - **Parameters** - `externref`: the dyntype context - `externref`: the value - **Return** - `i32`: result, 1 if it is a null, 0 otherwise - **dyntype_is_object** - **Description** - Check if the dynamic typed value is an object - **Parameters** - `externref`: the dyntype context - `externref`: the value - **Return** - `i32`: result, 1 if it is an object, 0 otherwise - **dyntype_is_array** - **Description** - Check if the dynamic typed value is an array - **Parameters** - `externref`: the dyntype context - `externref`: the value - **Return** - `i32`: result, 1 if it is an array, 0 otherwise - **dyntype_is_function** - **Description** - Check if the dynamic typed value is a function - **Parameters** - `externref`: the dyntype context - `externref`: the value - **Return** - `i32`: result, 1 if it is a function, 0 otherwise - **dyntype_is_extref** - **Description** - Check if the dynamic typed value is an extref - **Parameters** - `externref`: the dyntype context - `externref`: the value - **Return** - `i32`: result, 1 if it is an extref, 0 otherwise - **dyntype_to_number** - **Description** - Convert the dynamic typed value to a static number - **Parameters** - `externref`: the dyntype context - `externref`: the value - **Return** - `f64`: the converted number - **dyntype_to_bool** - **Description** - Convert the dynamic typed value to a static boolean - **Parameters** - `externref`: the dyntype context - `externref`: the value - **Return** - `i32`: the converted boolean - **dyntype_to_string** - **Description** - Convert the dynamic typed value to a static string - **Parameters** - `externref`: the dyntype context - `externref`: the value - **Return** - `structref`: the converted string - **dyntype_to_extref** - **Description** - Get the static object reference from extref - **Parameters** - `externref`: the dyntype context - `externref`: the extref value - **Return** - `i32`: the static object reference (wasm table element index) - **dyntype_is_exception** - **Description** - Check if the dynamic typed value is an exception - **Parameters** - `externref`: the dyntype context - `externref`: the value - **Return** - `i32`: result, 1 if it is an exception, 0 otherwise - **dyntype_is_falsy** - **Description** - Check if the dynamic typed value is a falsy value - **Parameters** - `externref`: the dyntype context - `externref`: the value - **Return** - `i32`: 1 if it is a falsy value, 0 otherwise - **dyntype_typeof** - **Description** - Get the type of the dynamic typed value - **Parameters** - `externref`: the dyntype context - `externref`: the value - **Return** - `structref`: the type of the value (WasmGC string) - **dyntype_type_eq** - **Description** - Check if the given dynamic typed objects are equal - **Parameters** - `externref`: the dyntype context - `externref`: the lhs value - `externref`: the lhs value - **Return** - `i32`: 1 if the object is equal, 0 otherwise - **dyntype_toString** - **Description** - Convert the dynamic typed value to a string - **Parameters** - `externref`: the dyntype context - `externref`: the value - **Return** - `structref`: the converted string (WasmGC string) - **dyntype_cmp** - **Description** - Compare two dynamic typed values - **Parameters** - `externref`: the dyntype context - `externref`: the lhs value - `externref`: the rhs value - `i32`: operator, 29 for `<`, 31 for `>`, 32 for `<=`, 33 for `>=`, 34 for `==`, 35 for `!=`, 36 for `===`, 37 for `!== - **Return** - `i32`: the comparison result, 1 for true, 0 otherwise - **dyntype_set_prototype** - **Description** - if it's a dynamic typed object: - Set the prototype of the dynamic typed object - if it's an extref: - raise exception: `libdyntype: unsupport operation for extref: set_prototype` - **Parameters** - `externref`: the dyntype context - `externref`: the object - `externref`: the prototype - **Return** - `i32`: result, 0 for success, -1 for failure - **dyntype_get_prototype** - **Description** - if it's a dynamic typed object: - Get the prototype of the dynamic typed object - if it's an extref: - raise exception: `libdyntype: unsupport operation for extref: get_prototype` - **Parameters** - `externref`: the dyntype context - `externref`: the object - **Return** - `externref`: the prototype - **dyntype_instanceof** - **Description** - Check if the dynamic typed value is an instance of the given value - **Parameters** - `externref`: the dyntype context - `externref`: the lhs value - `externref`: the rhs value - **Return** - `i32`: 1 if true, 0 otherwise - **dyntype_invoke** - **Description** - if it's a dynamic typed object: - Invoke method of the given dynamic object if name is not NULL, otherwise invoke the given dynamic object - if it's an extref: - Level 1: raise exception: `libdyntype: unsupport operation for extref: invoke` - Level 2: invoke the boxed wasm function (name is NULL) or class method (name is not NULL) - **Parameters** - `externref`: the dyntype context - `i32`: method name (string) - `externref`: the given dynamic object - `externref`: the arguments array - **Return** - `externref`: the return value - **dyntype_get_global** - **Description** - Get a global dynamic variable - **Parameters** - `externref`: the dyntype context - `i32`: global variable name (string) - **Return** - `externref`: the global variable - **dyntype_get_keys** - **Description** - Get the enumerable properties of the given object - **Parameters** - `externref`: the dyntype context - `externref`: the object - **Return** - `externref`: dynamic array which store all property names ================================================ FILE: doc/libstruct_indirect_api_spec.md ================================================ # libstruct_indirect API spec ## Overview The `libstruct_indirect` API is used to access WasmGC struct fields through index calculated during runtime. The API is designed to be used by `ts2wasm-compiler` to support `interface` type. These APIs are used to emulate the behaviour of the [proposed struct.get/set_indirect opcode](https://github.com/WebAssembly/gc/issues/397), if these opcodes are accepted by the proposal and supported by runtime, the `libstruct_indirect` API will no longer be required. ## Dependent status These APIs are required by `interface` type, the absence of this set of APIs would prevent apps utilizing `interface` type from functioning correctly. ## Module name `libstruct_indirect` ## API - **struct_get_indirect_i32** - **Description** - Get i32 field from WasmGC struct - **Parameters** - `structref`: the WasmGC struct - `i32`: field index - **Return** - `i32`: the field value - **struct_get_indirect_i64** - **Description** - Get i64 field from WasmGC struct - **Parameters** - `structref`: the WasmGC struct - `i32`: field index - **Return** - `i64`: the field value - **struct_get_indirect_f32** - **Description** - Get f32 field from WasmGC struct - **Parameters** - `structref`: the WasmGC struct - `i32`: field index - **Return** - `f32`: the field value - **struct_get_indirect_f64** - **Description** - Get f64 field from WasmGC struct - **Parameters** - `structref`: the WasmGC struct - `i32`: field index - **Return** - `f64`: the field value - **struct_get_indirect_anyref** - **Description** - Get anyref field from WasmGC struct - **Parameters** - `structref`: the WasmGC struct - `i32`: field index - **Return** - `anyref`: the field value - **struct_get_indirect_funcref** - **Description** - Get funcref field from WasmGC struct - **Parameters** - `structref`: the WasmGC struct - `i32`: field index - **Return** - `funcref`: the field value - **struct_set_indirect_i32** - **Description** - Set i32 field of WasmGC struct - **Parameters** - `structref`: the WasmGC struct - `i32`: field index - `i32`: field value - **struct_set_indirect_i64** - **Description** - Set i64 field of WasmGC struct - **Parameters** - `structref`: the WasmGC struct - `i32`: field index - `i64`: field value - **struct_set_indirect_f32** - **Description** - Set f32 field of WasmGC struct - **Parameters** - `structref`: the WasmGC struct - `i32`: field index - `f32`: field value - **struct_set_indirect_f64** - **Description** - Set f64 field of WasmGC struct - **Parameters** - `structref`: the WasmGC struct - `i32`: field index - `f64`: field value - **struct_set_indirect_anyref** - **Description** - Set anyref field of WasmGC struct - **Parameters** - `structref`: the WasmGC struct - `i32`: field index - `anyref`: field value - **struct_set_indirect_funcref** - **Description** - Set funcref field of WasmGC struct - **Parameters** - `structref`: the WasmGC struct - `i32`: field index - `funcref`: field value ================================================ FILE: doc/standard-library/array.md ================================================ # Array API The standard array APIs are implemented by `native`. Here we list the APIs supported by `Wasmnizer-ts`. Please note that specific implementations may differ slightly from libraries like JavaScript Core Library. For uniform APIs, hyperlinks to their descriptions will be provided. In situations where there are variations, this document offers API descriptions along with explanations for the differences. + [**`push(...items: T[]): number`**](https://github.com/microsoft/TypeScript/blob/eb374c28d6810e317b0c353d9b1330b0595458f4/src/lib/es5.d.ts#L1313-L1317) + [**`join(separator?: string): string`**](https://github.com/microsoft/TypeScript/blob/eb374c28d6810e317b0c353d9b1330b0595458f4/src/lib/es5.d.ts#L1330-L1334) + [**`reverse(): T[]`**](https://github.com/microsoft/TypeScript/blob/eb374c28d6810e317b0c353d9b1330b0595458f4/src/lib/es5.d.ts#L1335-L1339) + [**`slice(start?: number, end?: number): T[]`**](https://github.com/microsoft/TypeScript/blob/eb374c28d6810e317b0c353d9b1330b0595458f4/src/lib/es5.d.ts#L1345-L1354) + [**`unshift(...items: T[]): number`**](https://github.com/microsoft/TypeScript/blob/eb374c28d6810e317b0c353d9b1330b0595458f4/src/lib/es5.d.ts#L1381-L1385) + [**`indexOf(searchElement: T, fromIndex?: number): number`**](https://github.com/microsoft/TypeScript/blob/eb374c28d6810e317b0c353d9b1330b0595458f4/src/lib/es5.d.ts#L1386-L1391) + [**`lastIndexOf(searchElement: T, fromIndex?: number): number`**](https://github.com/microsoft/TypeScript/blob/eb374c28d6810e317b0c353d9b1330b0595458f4/src/lib/es5.d.ts#L1392-L1397) + [**`reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T`**](https://github.com/microsoft/TypeScript/blob/eb374c28d6810e317b0c353d9b1330b0595458f4/src/lib/es5.d.ts#L1449-L1455) + [**`reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T`**](https://github.com/microsoft/TypeScript/blob/eb374c28d6810e317b0c353d9b1330b0595458f4/src/lib/es5.d.ts#L1462-L1468) + [**`fill(value: T, start?: number, end?: number): T[]`**](https://github.com/microsoft/TypeScript/blob/eb374c28d6810e317b0c353d9b1330b0595458f4/src/lib/es2015.core.d.ts#L25-L33) + [**`includes(searchElement: T, fromIndex?: number): boolean`**](https://github.com/microsoft/TypeScript/blob/eb374c28d6810e317b0c353d9b1330b0595458f4/src/lib/es2016.array.include.d.ts#L2-L7) + **`pop(): T`** **Description**: Remove and return the last element from array. In `Wasmnizer-ts`, we will regard the union type `T | undefined` as any type, so we only define `T` as return type here. + **`concat(...items: T[]): T[]`** **Description**: Concatenate multiple arrays and returns a new array containing the merged elements. We simply set the `items`'s type to `T[]`. + **`shift(): T`** **Description**: Remove and return the first element from array. In `Wasmnizer-ts`, we will regard the union type `T | undefined` as any type, so we only define `T` as return type here. + **`sort(compareFn: (a: T, b: T) => number): T[]`** **Description**: Sort the elements of an array in place and return the sorted array by a comparison function `compareFn`. In `Wasmnizer-ts`, `this` represents class instance, so the return type is set to `T[]` not `this`. + **`splice(start: number, deleteCount?: number, ...items: T[]): T[]`** **Description**: Change the contents of an array by removing, replacing, or adding elements. Combines two standard APIs: `splice(start: number, deleteCount?: number): T[];` and `splice(start: number, deleteCount: number, ...items: T[]): T[];`. + **`every(predicate: (value: T, index: number, array: T[]) => boolean): boolean`** **Description**: Applies a provided callback function `predicate` to each element in the array and returns `true` if the callback returns `true` for every element, otherwise, it returns `false`. We set the `predicate callback function`'s return type to boolean, and the second parameter `thisArg` in the standard library has been deleted. + **`some(predicate: (value: T, index: number, array: T[]) => boolean): boolean`** **Description**: Applies a provided callback function `predicate` to each element in the array and returns `true` if the callback returns `true` for at least one element, otherwise, it returns `false`. We set the `predicate callback function`'s return type to boolean, and the second parameter `thisArg` in the standard library has been deleted. + **`forEach(callbackfn: (value: T, index: number, array: T[]) => void): void`** **Description**: Iterate over the elements of an array and apply provided callback function `callbackfn` to each element. The second parameter `thisArg` in the standard library has been deleted. + **`map(callbackfn: (value: T, index: number, array: T[]) => U): U[]`** **Description**: Return a new array containing the results of applying the callback function `callbackfn` to each element. The second parameter `thisArg` in the standard library has been deleted. + **`filter(predicate: (value: T, index: number, array: T[]) => boolean): T[]`** **Description**: Return a new array containing elements that satisfy the condition specified in the callback function `predicate`. We set the `predicate callback function`'s return type to boolean, and the second parameter `thisArg` in the standard library has been deleted. + `find(predicate: (value: T, index: number, obj: T[]) => boolean): any`** **Description**: Search for and return the first element in an array that satisfies a specified condition defined by a provided callback function `predicate`, otherwise, return `undefined`. We set the `predicate callback function`'s return type to boolean, the second parameter `thisArg` in the standard library has been deleted, since `find` is always return `undefined`, so we set `any` type to represent `T | undefined`. + **`findIndex(predicate: (value: T, index: number, obj: T[]) => boolean): number`** **Description**: Return the index of the first element in an array that satisfies a specified condition defined by a provided callback function `predicate`, otherwise, return -1. We set the `predicate callback function`'s return type to boolean, and the second parameter `thisArg` in the standard library has been deleted. + **`copyWithin(target: number, start: number, end?: number): T[]`** **Description**: Copy a portion of an array to another location within the same array. In `Wasmnizer-ts`, `this` represents class instance, so the return type is set to `T[]` not `this`. ================================================ FILE: doc/standard-library/console.md ================================================ # console API - **[`log(...values: any[]): void`](https://github.com/microsoft/TypeScript/blob/c532603633178c552b9747eef057784db2fc1e23/src/lib/dom.generated.d.ts#L26432C1-L26433C31), `native`** Print values to console. If the argument is statically typed, it will be auto boxed to any. ================================================ FILE: doc/standard-library/index.md ================================================ # Wasmnizer-ts standard library TypeScript doesn't introduce its own standard library, instead it relies on APIs provided by JavaScript engine because it is originally designed to be transpiled to JavaScript. In `Wasmnizer-ts`, there are both static and dynamic type system - For dynamic part, the [fallback mechanism](../developer-guide/fallback.md) allow us to re-use the standard library from external environment. - For static part, we need to implement standard library based on static object layout. Three methods are used to implement standard library in `Wasmnizer-ts`: 1. **native**: implement standard library in native, and expose them to wasm module through host APIs. 2. **source code**: implement standard library in TypeScript source code, and compile them with application code together. 3. **binaryen API**: implement standard library in wasm bytecode through binaryen API, and link them with application code together. If the standard library API is implemented in `native`, then corresponding `libstd API` is required; if the standard library API is implemented in `source code` or `binaryen API`, then will be contained inside generated wasm module, and don't requre any APIs from runtime environment. Please navigate to below pages to check the API list and implementation method for every APIs. If those APIs differ from the Core Library or Standard Library, there will be a description provided below to explain its usage. - [console](./console.md) - [string](./string.md) - [array](./array.md) - [math](./math.md) ================================================ FILE: doc/standard-library/math.md ================================================ # Math API The standard math APIs are implemented by `source code` and `binaryen API`. Here we list the APIs supported by `Wasmnizer-ts`. + [**`pow(x: number, y: number): number`**](https://github.com/microsoft/TypeScript/blob/eb374c28d6810e317b0c353d9b1330b0595458f4/src/lib/es5.d.ts#L712-L717) + [**`max(...values: number[]): number`**](https://github.com/microsoft/TypeScript/blob/eb374c28d6810e317b0c353d9b1330b0595458f4/src/lib/es5.d.ts#L702-L706) + [**`min(...values: number[]): number`**](https://github.com/microsoft/TypeScript/blob/eb374c28d6810e317b0c353d9b1330b0595458f4/src/lib/es5.d.ts#L707-L711) + [**`sqrt(x: number): number`**](https://github.com/microsoft/TypeScript/blob/eb374c28d6810e317b0c353d9b1330b0595458f4/src/lib/es5.d.ts#L730-L734) + [**`abs(x: number): number`**](https://github.com/microsoft/TypeScript/blob/eb374c28d6810e317b0c353d9b1330b0595458f4/src/lib/es5.d.ts#L651-L655) + [**`ceil(x: number): number`**](https://github.com/microsoft/TypeScript/blob/eb374c28d6810e317b0c353d9b1330b0595458f4/src/lib/es5.d.ts#L677-L681) + [**`floor(x: number): number`**](https://github.com/microsoft/TypeScript/blob/eb374c28d6810e317b0c353d9b1330b0595458f4/src/lib/es5.d.ts#L692-L696) ================================================ FILE: doc/standard-library/string.md ================================================ # string API The standard string APIs are implemented by `binaryen API`. Below are the string APIs supported by `Wasmnizer-ts`. Please note that specific implementations may differ slightly from libraries like JavaScript Core Library. For uniform APIs, hyperlinks to their descriptions will be provided. In situations where there are variations, this document offers API descriptions along with explanations for the differences. + **[`concat(...strings: string[]): string`](https://github.com/microsoft/TypeScript/blob/c532603633178c552b9747eef057784db2fc1e23/src/lib/es5.d.ts#L408C1-L412C42)** + **[`slice(start?: number, end?: number): string`](https://github.com/microsoft/TypeScript/blob/c532603633178c552b9747eef057784db2fc1e23/src/lib/es5.d.ts#L460C1-L466C49)** + **[`charAt(pos: number): string`](https://github.com/microsoft/TypeScript/blob/c532603633178c552b9747eef057784db2fc1e23/src/lib/es5.d.ts#L396C1-L400C33)** + **[`charCodeAt(index: number): number`](https://github.com/microsoft/TypeScript/blob/c532603633178c552b9747eef057784db2fc1e23/src/lib/es5.d.ts#L402C1-L406C39)** + **[`substring(start: number, end?: number): string`](https://github.com/microsoft/TypeScript/blob/c532603633178c552b9747eef057784db2fc1e23/src/lib/es5.d.ts#L475C1-L481C52)** + **[`trim(): string`](https://github.com/microsoft/TypeScript/blob/c532603633178c552b9747eef057784db2fc1e23/src/lib/es5.d.ts#L495C1-L496C20)** + **[`toLowerCase(): string`](https://github.com/microsoft/TypeScript/blob/c532603633178c552b9747eef057784db2fc1e23/src/lib/es5.d.ts#L483C1-L484C27)** + **[`toUpperCase(): string`](https://github.com/microsoft/TypeScript/blob/c532603633178c552b9747eef057784db2fc1e23/src/lib/es5.d.ts#L489C1-L490C27)** + **`indexOf(searchString: string): number`** **Description**: Searches for the first occurrence of a specified substring (`searchString`) within a string and returns the index at which it is found, otherwise, returns -1. Passing the starting search index has not been implemented yet. + **`lastIndexOf(str: string): number`** **Description**: Searches for the last occurrence of a specified substring (`str`) within a string and returns the index (position) at which it is found, otherwise, returns -1. Passing the starting search index has not been implemented yet. + **`split(sep: string): string[]`** **Description**: Splits a string into an array of substrings based on a specified separator (`sep`). `RegExp` has not been implemented yet. + **`replace(from: string, to: string): string`** **Description**: Replaces **the first** occurrence of a specified substring (`from`) within a string with another substring (`to`) and returns the resulting modified string. `RegExp` has not been implemented yet. + **`match(pattern: string): string[]`** **Description**: Searches for **the first** occurrence of a specified regular expression (`pattern`) within a string and returns an array containing the matched substring. `RegExp` has not been implemented yet. + **`search(pattern: string): number`** **Description**: Searches for a specified regular string (`pattern`) within a string and returns the index of the first occurrence of the matched substring or -1 if no match is found. `RegExp` has not been implemented yet. ================================================ FILE: doc/validation_strategy.md ================================================ # ts2wasm-compiler validation strategy (WIP) Ts2wasm-compiler aims to compile TypeScript source code to WebAssembly bytecode while keep the same semantics, the default validation strategy is to compile the same TypeScript source code into both JavaScript (through `tsc`) and WebAssembly (through `ts2wasm-compiler`) and compare the execution results. ![](./img/validation_strategy.excalidraw.png) ## Test case organization Test cases are organized into three levels: 1. category - A category is a collection of test suites serving the same goal (e.g. syntax, stdlib, etc) - Most categories used default validation strategy as shown above, some special categories may have special strategy and they can have their own test framework to execute the cases and validate the results 2. suite - A suite is a folder containing several sub-folders or files for testing a language feature (e.g. syntax::BinaryenExpression) - Only folders directly in the category root dir is a suite, sub-folders inside a suite is not a new suite. Validators can use up to **two level** of sub-folders according to the complex of the tested feature, but it's recommended to keep a light folder structure - Suite should be automatically detected by the test framework of the corresponding category 3. case - A `standalone` case is an `exported function` inside a source file, it should return something or print something as the result - `multi-file` cases are stored in an folder containing an `index.ts` as the entry file, it can import any other files inside the `folder` or any level of `sub-folders` - It's allowed to write several cases inside a single file, but the cases with side effects must be put into one file to avoid influence on other files ## Test case composition ``` TypeScript /// compile options: xxx /// auto_parameter: none | single | matrix export binaryExpressionAdd(x: number, y: number) { return x + y; } export binaryExpressionSub(x: number, y: number) { return x - y; } ``` 1. Each file can optionally have `front matter` to interact with the `test framework` - There may be multiple lines in the front matter, each line starts with `///` token - A file is the minimal compilation unit, so the front matter apply to all the cases in a file - There are some builtin configs which will be automatically consumed by all test frameworks (such as `compile options`, `auto_parameter`), other configs are defined and consumed by the test framework of each categories > Note: per-case configuration may be supported in the future if required. 2. Parameters must be `number` or `boolean`, we may support `string` and other type later - The test framework may randomly generate a group or several groups of parameters as input 3. The test case body can use any supported syntax, but the return type (if has) should be `number` or `boolean`, otherwise the test framework won't be able to validate the result > If you need to validate complex objects content, use `console.log` rather than `return` 4. A separate json file (with same filename) can be placed besides to the case file to record fixed parameters - Test framework should always execute based on the fixed parameters (if exists) firstly. (**This ensure the determinacy**) - If `auto_parameter` is not none, then test framework automatically generate some parameters as input, and record the parameters in the log if failed. (**This helps to discover more corner cases**) ``` json [ { "case": "xxxx", "args": [ { "x": 1, "y": 2 }, { "x": 100, "y": 1000 } ] } ] ``` ## Reference folder structure ``` Bash tests/ |--syntax/ |--binary-expression/ |--add.ts |--add.json |--sub.ts |--assign.ts |--property-access-expression/ |--call-expression/ |--if-statement/ |--loop-statement/ |--... |--stdlib/ |--array/ |--string/ |--fallthrough |--map/ |--JSON/ ``` ================================================ FILE: example/app-framework/CMakeLists.txt ================================================ # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception cmake_minimum_required (VERSION 2.9) project (simple) ################ wamr runtime settings ################ message(STATUS "WAMR_BUILD_SDK_PROFILE=${WAMR_BUILD_SDK_PROFILE}") # Reset default linker flags set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") if ("$ENV{COLLECT_CODE_COVERAGE}" STREQUAL "1" OR COLLECT_CODE_COVERAGE EQUAL 1) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") endif () set (RUNTIMR_DIR ${CMAKE_CURRENT_LIST_DIR}/../../runtime-library) set (WAMR_ROOT_DIR ${RUNTIMR_DIR}/deps/wamr-gc) ## use library and headers in the SDK link_directories(${WAMR_ROOT_DIR}/wamr-sdk/out/${WAMR_BUILD_SDK_PROFILE}/runtime-sdk/lib) include_directories( ${WAMR_ROOT_DIR}/wamr-sdk/out/${WAMR_BUILD_SDK_PROFILE}/runtime-sdk/include ${WAMR_ROOT_DIR}/core/shared/utils ${WAMR_ROOT_DIR}/core/shared/platform/linux ) ################ application related ################ include_directories(${CMAKE_CURRENT_LIST_DIR}/src) #Note: uncomment below line to use UART mode #add_definitions (-DCONNECTION_UART) ## wamr # include(${CMAKE_CURRENT_LIST_DIR}/wamr_config_wasmnizer_ts.cmake) # add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) set (WAMR_BUILD_PLATFORM "linux") set (WAMR_BUILD_TARGET X86_64) set (WAMR_BUILD_INTERP 1) set (WAMR_BUILD_AOT 1) set (WAMR_BUILD_JIT 0) set (WAMR_BUILD_LIBC_BUILTIN 1) set (WAMR_BUILD_LIBC_WASI 0) set (WAMR_BUILD_GC 1) set (WAMR_BUILD_GC_BINARYEN 1) set (WAMR_BUILD_STRINGREF 1) set (USE_SIMPLE_LIBDYNTYPE 1) ## stringref set(STRINGREF_DIR ${RUNTIMR_DIR}/stringref) set(WAMR_STRINGREF_IMPL_SOURCE ${STRINGREF_DIR}/stringref_simple.c ) include(${RUNTIMR_DIR}/deps/wamr-gc/build-scripts/runtime_lib.cmake) ## libdyntype set(LIBDYNTYPE_DIR ${RUNTIMR_DIR}/libdyntype) include (${LIBDYNTYPE_DIR}/libdyntype.cmake) ## stdlib set(STDLIB_DIR ${RUNTIMR_DIR}/stdlib) include_directories(${STDLIB_DIR}) set(STDLIB_SOURCE ${STDLIB_DIR}/lib_console.c ${STDLIB_DIR}/lib_array.c ${STDLIB_DIR}/lib_timer.c ) ## struct-indirect set(STRUCT_INDIRECT_DIR ${RUNTIMR_DIR}/struct-indirect) include_directories(${STRUCT_INDIRECT_DIR}) set(STRUCT_INDIRECT_SOURCE ${STRUCT_INDIRECT_DIR}/lib_struct_indirect.c ) ## utils set(UTILS_DIR ${RUNTIMR_DIR}/utils) include_directories(${UTILS_DIR}) set(TYPE_UTILS_SOURCE ${UTILS_DIR}/type_utils.c ) set(OBJECT_UTILS_SOURCE ${UTILS_DIR}/object_utils.c ) set(WAMR_UTILS_SOURCE ${UTILS_DIR}/wamr_utils.c ) # Ignore warnings of QuickJS set_source_files_properties( ${QUICKJS_SOURCE} PROPERTIES COMPILE_FLAGS "-w" ) add_executable (simple src/main.c src/iwasm_main.c ${QUICKJS_SOURCE} ${LIBDYNTYPE_SRC} ${STDLIB_SOURCE} ${STRUCT_INDIRECT_SOURCE} ${TYPE_UTILS_SOURCE} ${OBJECT_UTILS_SOURCE} ${WAMR_UTILS_SOURCE} ) target_link_libraries (simple vmlib -lm -ldl -lpthread -lrt) ================================================ FILE: example/app-framework/app/connection.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import * as connection from '../lib/connection'; import * as timer from '../lib/timer'; import * as request from '../lib/request'; import * as attr_container from '../lib/attr_container'; let num = 0; let g_conn: connection.wamr_connection | null; function timer1_update(my_timer: timer.user_timer) { const message = 'Hello, ' + num; num++; connection.send_on_connection(g_conn!, message, message.length); } export function on_init(): void { request.register_resource_handler('/close', (req) => { if (g_conn !== null) { timer.timer_cancel(my_timer); connection.close_connection(g_conn); } const resp = request.make_response_for_request(req); resp.set_response(request.CoAP_Status.DELETED_2_02, 0, null, 0); request.api_response_send(resp); }); let args = attr_container.attr_container_create(''); let isSuccess = attr_container.attr_container_set_string( args, 'address', '127.0.0.1', ); if (isSuccess) { args = attr_container.global_attr_cont; } isSuccess = attr_container.attr_container_set_uint16(args, 'port', 7777); if (isSuccess) { args = attr_container.global_attr_cont; } g_conn = connection.open_connection( 'TCP', args, (conn, type, data, len) => { if (type == connection.CONN_EVENT_TYPE_DATA) { const message = data; console.log('Client got a message from server ->' + message); } else if (type == connection.CONN_EVENT_TYPE_DISCONNECT) { console.log('connection is close by server!'); } else { console.log('error: got unknown event type!!!'); } }, ); if (g_conn == null) { console.log('connect to server fail!'); return; } console.log('connect to server success!'); // const my_timer = new timer.user_timer(timer1_update, 1000, true); // timer.timer_restart(my_timer, 1000); const my_timer = timer.setInterval(timer1_update, 2000); } export function on_destroy(): void { // on destory actions } ================================================ FILE: example/app-framework/app/request_handler.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import * as request from '../lib/request'; import { arraybuffer_to_string, string_to_arraybuffer } from '../lib/utils'; export function on_init(): void { request.register_resource_handler('/test', (req) => { const payload_string = arraybuffer_to_string( req.payload, req.payload_len, ); console.log('### Req: /test ' + payload_string); console.log(' request payload:'); console.log(' ' + payload_string + '\n'); const resp = request.make_response_for_request(req); resp.set_payload(string_to_arraybuffer('OK'), 2); request.api_response_send(resp); }); } export function on_destroy(): void { // on destory actions } ================================================ FILE: example/app-framework/app/request_sender.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import * as request from '../lib/request'; import { arraybuffer_to_string, string_to_arraybuffer } from '../lib/utils'; export function on_init(): void { const payload = string_to_arraybuffer('hello, handler'); request.post('/test', payload, payload.byteLength, '', (resp) => { if (resp != null) { console.log('Post Success'); if (resp.payload != null) { console.log(' response payload:'); const resp_payload_string = arraybuffer_to_string( resp.payload, resp.payload_len, ); console.log(' ' + resp_payload_string + '\n'); } } else console.log('Post Timeout'); }); } export function on_destroy(): void { // on destory actions } ================================================ FILE: example/app-framework/app/timer.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import * as timer from '../lib/timer'; let cnt = 0; export function on_init(): void { /* The callback function will be called every 2 second, and will stop after 10 calls */ const my_timer = timer.setInterval((my_timer) => { cnt++; console.log((cnt * 2).toString() + ' seconds passed'); if (cnt >= 10) { timer.timer_cancel(my_timer); console.log('Stop Timer'); } }, 2000); } export function on_destroy(): void { // on destory actions } ================================================ FILE: example/app-framework/build.sh ================================================ # # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # #!/bin/bash CURR_DIR=$PWD WAMR_DIR=${PWD}/../../runtime-library/deps/wamr-gc PROFILE_DIR=${PWD}/profiles OUT_DIR=${PWD}/out WASM_APPS=${PWD}/app CLEAN= CM_BUILD_TYPE="-DCMAKE_BUILD_TYPE=Release" CM_TOOLCHAIN="" TS2WASM_SCRIPT=${PWD}/../../build/cli/ts2wasm.js OPT_LEVEL=3 WAMRC=${WAMR_DIR}/wamr-compiler/build/wamrc usage () { echo "build.sh [options]" echo " -p [profile]" echo " -d [target]" echo " -c, rebuild SDK" exit 1 } while getopts "p:dch" opt do case $opt in p) PROFILE=$OPTARG ;; d) CM_BUILD_TYPE="-DCMAKE_BUILD_TYPE=Debug" ;; c) CLEAN="TRUE" ;; h) usage exit 1; ;; ?) echo "Unknown arg: $arg" usage exit 1 ;; esac done if [ "$CLEAN" = "TRUE" ]; then rm -rf $CURR_DIR/cmake-build fi while [ ! -n "$PROFILE" ] do support_profiles=`ls -l "${PROFILE_DIR}" |grep '^d' | awk '{print $9}'` read -p "Enter build target profile (default=host-interp) --> $support_profiles \>:" read_platform if [ ! -n "$read_platform" ]; then PROFILE="host-interp" else PROFILE=$read_platform fi done ARG_TOOLCHAIN="" TOOL_CHAIN_FILE=$PROFILE_DIR/$PROFILE/toolchain.cmake if [ -f $TOOL_CHAIN_FILE ]; then CM_TOOLCHAIN="-DCMAKE_TOOLCHAIN_FILE=$TOOL_CHAIN_FILE" ARG_TOOLCHAIN="-t $TOOL_CHAIN_FILE" echo "toolchain file: $TOOL_CHAIN_FILE" fi SDK_CONFIG_FILE=$PROFILE_DIR/$PROFILE/wamr_config_wasmnizer_ts.cmake if [ ! -f $SDK_CONFIG_FILE ]; then echo "SDK config file [$SDK_CONFIG_FILE] doesn't exit. quit.." exit 1 fi rm -rf ${OUT_DIR} mkdir ${OUT_DIR} mkdir ${OUT_DIR}/wasm-apps cd ${WAMR_DIR}/core/shared/mem-alloc PROFILE="simple-$PROFILE" echo "#####################build wamr sdk" cd ${WAMR_DIR}/wamr-sdk ./build_sdk.sh -n $PROFILE -x $SDK_CONFIG_FILE $ARG_TOOLCHAIN -c [ $? -eq 0 ] || exit $? echo "#####################build simple project" cd ${CURR_DIR} mkdir -p cmake-build/$PROFILE cd cmake-build/$PROFILE cmake ../.. -DWAMR_BUILD_SDK_PROFILE=$PROFILE $CM_TOOLCHAIN $CM_BUILD_TYPE make if [ $? != 0 ];then echo "BUILD_FAIL simple exit as $?\n" exit 2 fi cp -a simple ${OUT_DIR} echo "#####################build simple project success" echo -e "\n\n" echo "#####################build host-tool" cd ${WAMR_DIR}/test-tools/host-tool mkdir -p bin cd bin cmake .. $CM_TOOLCHAIN $CM_BUILD_TYPE make if [ $? != 0 ];then echo "BUILD_FAIL host tool exit as $?\n" exit 2 fi cp host_tool ${OUT_DIR} echo "#####################build host-tool success" echo -e "\n\n" echo "#####################build wasm apps" cd ${WASM_APPS} for i in `ls *.ts` do APP_SRC="$i" OUT_WASM_FILE=${i%.*}.wasm OUT_AOT_FILE=${i%.*}.aot node ${TS2WASM_SCRIPT} ${APP_SRC} --opt ${OPT_LEVEL} --output ${OUT_DIR}/wasm-apps/${OUT_WASM_FILE} --startSection > tmp.txt $WAMRC --enable-gc -o ${OUT_DIR}/wasm-apps/${OUT_AOT_FILE} ${OUT_DIR}/wasm-apps/${OUT_WASM_FILE} > tmp.txt if [ -f ${OUT_DIR}/wasm-apps/${OUT_WASM_FILE} ]; then echo "build ${OUT_WASM_FILE} success" else echo "build ${OUT_WASM_FILE} fail" fi if [ -f ${OUT_DIR}/wasm-apps/${OUT_AOT_FILE} ]; then echo "build ${OUT_AOT_FILE} success" else echo "build ${OUT_AOT_FILE} fail" fi done rm tmp.txt echo "#####################build wasm apps done" ================================================ FILE: example/app-framework/lib/attr_container.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { i32, arraybuffer_to_string, string_to_arraybuffer } from './utils'; const ATTR_TYPE_BEGIN = 0, ATTR_TYPE_BYTE = 0, ATTR_TYPE_INT8 = 0, ATTR_TYPE_SHORT = 0, ATTR_TYPE_INT16 = 1, ATTR_TYPE_INT = 2, ATTR_TYPE_INT32 = 2, ATTR_TYPE_INT64 = 3, ATTR_TYPE_UINT8 = 4, ATTR_TYPE_UINT16 = 5, ATTR_TYPE_UINT32 = 6, ATTR_TYPE_UINT64 = 7, /** * Why ATTR_TYPE_FLOAT = 10? * We determine the number of bytes that should be copied through 1<<(type & * 3). ATTR_TYPE_BYTE = 0, so the number of bytes is 1 << 0 = 1. * ATTR_TYPE_UINT64 = 7, so the number of bytes is 1 << 3 = 8. * Since the float type takes up 4 bytes, ATTR_TYPE_FLOAT should be 10. * Calculation: (1 << (10&3)) = (1 << 2) = 4 */ ATTR_TYPE_FLOAT = 10, ATTR_TYPE_DOUBLE = 11, ATTR_TYPE_BOOLEAN = 12, ATTR_TYPE_STRING = 13, ATTR_TYPE_BYTEARRAY = 14, ATTR_TYPE_END = 14; const ATTR_CONT_READONLY_SHIFT = 2; export let global_attr_cont: ArrayBuffer; export function attr_container_create(tag: string) { const tag_length = tag.length + 1; const offset_of_buf = 2; const length = offset_of_buf + 4 + 2 + tag_length + 100; const attr_buffer = new ArrayBuffer(length); const dataview = new DataView(attr_buffer); for (let i = 0; i < length; i++) { dataview.setUint8(i, 0); } let offset = offset_of_buf; dataview.setUint32(offset, length - offset_of_buf, true); offset += 4; dataview.setUint16(offset, tag_length, true); offset += 2; for (let i = 0; i < tag.length; i++) { dataview.setUint8(i + offset, tag.charCodeAt(i)); } return attr_buffer; } export function attr_container_get_serialize_length(args: ArrayBuffer): i32 { const dataview = new DataView(args); const buf_value = dataview.getUint32(2, true); return 2 + buf_value; } export function attr_container_set_string( attr_cont: ArrayBuffer, key: string, value: string, ) { const value_buffer = string_to_arraybuffer(value); return attr_container_set_attr( attr_cont, key, ATTR_TYPE_STRING, value_buffer, value.length + 1, ); } export function attr_container_set_uint16( attr_cont: ArrayBuffer, key: string, value: number, ) { const value_buffer = new ArrayBuffer(2); const value_dataview = new DataView(value_buffer); value_dataview.setUint16(0, value, true); return attr_container_set_attr( attr_cont, key, ATTR_TYPE_UINT16, value_buffer, 2, ); } function check_set_attr(p_attr_cont: ArrayBuffer, key: string) { if (key.length === 0) { console.log('Set attribute failed: invalid input arguments.'); return false; } const dataview = new DataView(p_attr_cont); const flags = dataview.getUint32(0, true); if (flags & ATTR_CONT_READONLY_SHIFT) { console.log('Set attribute failed: attribute container is readonly.'); return false; } return true; } export function attr_container_get_attr_begin(attr_cont: ArrayBuffer) { let p = 2; const dataview = new DataView(attr_cont); /* skip total length */ const total_length = dataview.getUint32(p, true); p += 4; /* tag length */ const str_len = dataview.getUint16(p, true); p += 2; /* tag content */ p += str_len; /* attribute num */ const attr_num = dataview.getUint16(p, true); p += 2; return p; } export function attr_container_get_attr_total_length(attr_cont: ArrayBuffer) { const p = 2; const dataview = new DataView(attr_cont); const total_length = dataview.getUint32(p, true); return total_length; } export function attr_container_get_attr_num(attr_cont: ArrayBuffer) { let p = 2; const dataview = new DataView(attr_cont); /* skip total length */ const total_length = dataview.getUint32(p, true); p += 4; /* tag length */ const str_len = dataview.getUint16(p, true); p += 2; /* tag content */ p += str_len; /* attribute num */ const attr_num = dataview.getUint16(p, true); p += 2; return attr_num; } export function attr_container_get_msg_end(attr_cont: ArrayBuffer) { const p = 2; const dataview = new DataView(attr_cont); return p + dataview.getUint32(p, true); } export function attr_container_get_attr_next( attr_cont: ArrayBuffer, curr_attr_pos: number, ) { let p = curr_attr_pos; const dataview = new DataView(attr_cont); /* key length and key */ p += 2 + dataview.getUint16(p, true); const type = dataview.getUint8(p); p++; /* Byte type to Boolean type */ if (type >= ATTR_TYPE_BYTE && type <= ATTR_TYPE_BOOLEAN) { p += 1 << (type & 3); return p; } else if (type == ATTR_TYPE_STRING) { /* String type */ p += 2 + dataview.getUint16(p, true); return p; } else if (type == ATTR_TYPE_BYTEARRAY) { /* ByteArray type */ p += 4 + dataview.getUint32(p, true); return p; } } export function attr_container_get_attr_end(attr_cont: ArrayBuffer) { let p = attr_container_get_attr_begin(attr_cont); const attr_num = attr_container_get_attr_num(attr_cont); for (let i = 0; i < attr_num; i++) { const tmp = attr_container_get_attr_next(attr_cont, p); if (tmp === undefined) { return -1; } else { p = tmp; } } return p; } export function attr_container_find_attr(attr_cont: ArrayBuffer, key: string) { let p = 2; p = attr_container_get_attr_begin(attr_cont); const attr_num = attr_container_get_attr_num(attr_cont); const dataview = new DataView(attr_cont); for (let i = 0; i < attr_num; i++) { const str_len = dataview.getUint16(p, true); if (str_len == key.length + 1) { for (let i = 0; i < key.length; i++) { dataview.setUint8(p + 2 + i, key.charCodeAt(i)); } /* string length also includes /0 in C */ dataview.setUint8(p + str_len - 1, 0); return p; } const tmp = attr_container_get_attr_next(attr_cont, p); if (tmp === undefined) { return -1; } else { p = tmp; } } return -1; } function attr_container_inc_attr_num(attr_cont: ArrayBuffer) { /* skip total length */ let p = 2 + 4; const dataview = new DataView(attr_cont); const str_len = dataview.getUint16(p, true); /* skip tag length and tag */ p += 2 + str_len; /* attribute num */ const attr_num = dataview.getUint16(p, true) + 1; dataview.setUint16(p, attr_num, true); } export function attr_container_set_attr( attr_cont: ArrayBuffer, key: string, type: number, value: ArrayBuffer, value_length: number, ) { if (!check_set_attr(attr_cont, key)) { return false; } let p = 2; const dataview = new DataView(attr_cont); const value_dataview = new DataView(value); let total_length = dataview.getUint32(p, true); const attr_end = attr_container_get_attr_end(attr_cont); if (attr_end === -1) { console.log('Set attr failed: get attr end failed.'); return false; } const msg_end = attr_container_get_msg_end(attr_cont); /* key len + key + '\0' + type */ let attr_len = 2 + key.length + 1 + 1; if (type >= ATTR_TYPE_BYTE && type <= ATTR_TYPE_BOOLEAN) attr_len += 1 << (type & 3); else if (type == ATTR_TYPE_STRING) attr_len += 2 + value_length; else if (type == ATTR_TYPE_BYTEARRAY) attr_len += 4 + value_length; const attr_buf = new ArrayBuffer(attr_len); const attr_buf_dataview = new DataView(attr_buf); p = 0; /* Set the attr buf */ const str_len = key.length + 1; attr_buf_dataview.setUint16(p, str_len, true); p += 2; for (let i = 0; i < key.length; i++) { attr_buf_dataview.setUint8(i + p, key.charCodeAt(i)); } /* string length also includes /0 in C */ attr_buf_dataview.setUint8(p + str_len - 1, 0); p += str_len; attr_buf_dataview.setUint8(p, type); p++; if (type >= ATTR_TYPE_BYTE && type <= ATTR_TYPE_BOOLEAN) { const len = 1 << (type & 3); for (let i = 0; i < len; i++) { attr_buf_dataview.setUint8(p + i, value_dataview.getUint8(i)); } } else if (type == ATTR_TYPE_STRING) { attr_buf_dataview.setUint16(p, value_length, true); p += 2; for (let i = 0; i < value_dataview.byteLength; i++) { attr_buf_dataview.setUint8(p + i, value_dataview.getUint8(i)); } /* string length also includes /0 in C */ attr_buf_dataview.setUint8(p + value_length - 1, 0); } else if (type == ATTR_TYPE_BYTEARRAY) { attr_buf_dataview.setUint32(p, value_length, true); p += 4; for (let i = 0; i < value_dataview.byteLength; i++) { attr_buf_dataview.setUint8(p + i, value_dataview.getUint8(i)); } /* string length also includes /0 in C */ attr_buf_dataview.setUint8(p + value_length - 1, 0); } p = attr_container_find_attr(attr_cont, key); if (p !== -1) { /* key found */ const p1 = attr_container_get_attr_next(attr_cont, p)!; if (p1 - p == attr_len) { for (let i = 0; i < attr_len; i++) { dataview.setUint8(i + p, attr_buf_dataview.getUint8(i)); } global_attr_cont = attr_cont; return true; } if (p1 - p + msg_end - attr_end >= attr_len) { for (let i = 0; i < attr_end - p1; i++) { dataview.setUint8(p + i, dataview.getUint8(p1 + i)); } for (let i = 0; i < attr_len; i++) { dataview.setUint8( i + p + (attr_end - p1), attr_buf_dataview.getUint8(i), ); } global_attr_cont = attr_cont; return true; } total_length += attr_len + 100; const attr_cont1 = new ArrayBuffer(2 + total_length); const attr_cont1_dataview = new DataView(attr_cont1); for (let i = 0; i < p; i++) { attr_cont1_dataview.setUint8(i, dataview.getUint8(i)); } for (let i = 0; i < attr_end - p1; i++) { attr_cont1_dataview.setUint8(i + p, dataview.getUint8(i + p1)); } for (let i = 0; i < attr_len; i++) { attr_cont1_dataview.setUint8( i + p + attr_end - p1, attr_buf_dataview.getUint8(i), ); } p = 2; attr_cont1_dataview.setUint32(p, total_length, true); global_attr_cont = attr_cont1; return true; } else { /* key not found */ if (msg_end - attr_end >= attr_len) { for (let i = 0; i < attr_len; i++) { dataview.setUint8(i + attr_end, attr_buf_dataview.getUint8(i)); } attr_container_inc_attr_num(attr_cont); global_attr_cont = attr_cont; return true; } total_length += attr_len + 100; const attr_cont1 = new ArrayBuffer(2 + total_length); const attr_cont1_dataview = new DataView(attr_cont1); for (let i = 0; i < attr_end; i++) { attr_cont1_dataview.setUint8(i, dataview.getUint8(i)); } for (let i = 0; i < attr_len; i++) { attr_cont1_dataview.setUint8( i + attr_end, attr_buf_dataview.getUint8(i), ); } attr_container_inc_attr_num(attr_cont1); p = 2; attr_cont1_dataview.setUint32(p, total_length, true); global_attr_cont = attr_cont1; return true; } } ================================================ FILE: example/app-framework/lib/connection.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { i32, arraybuffer_to_string, string_to_arraybuffer } from './utils'; import { attr_container_get_serialize_length } from './attr_container'; // Wasmnizer-ts: @NativeSignature@ (i32, i32, i32)=>boolean declare function wasm_open_connection( name: string, args_buf: ArrayBuffer, args_buf_len: i32, ): i32; declare function wasm_close_connection(handle: i32): void; // Wasmnizer-ts: @NativeSignature@ (i32, i32, i32)=>boolean declare function wasm_send_on_connection( handle: i32, data: ArrayBuffer, data_len: i32, ): i32; // Wasmnizer-ts: @NativeSignature@ (i32, i32, i32)=>boolean declare function wasm_config_connection( handle: i32, cfg_buf: ArrayBuffer, cfg_buf_len: i32, ): i32; export const /* Data is received */ CONN_EVENT_TYPE_DATA = 1, /* Connection is disconnected */ CONN_EVENT_TYPE_DISCONNECT = 2; export type on_connection_event_f = ( conn: wamr_connection, type: number, data: string, len: number, ) => void; export class wamr_connection { handle: i32; on_event: on_connection_event_f; constructor(handle: i32, on_event: on_connection_event_f) { this.handle = handle; this.on_event = on_event; } } const connection_list = new Array(); export function open_connection( name: string, args: ArrayBuffer, on_event: on_connection_event_f, ): wamr_connection | null { const args_len: i32 = attr_container_get_serialize_length(args); const handle = wasm_open_connection(name, args, args_len); if (handle === -1) { return null; } const conn = new wamr_connection(handle, on_event); connection_list.push(conn); return conn; } export function close_connection(c: wamr_connection) { for (let i = 0; i < connection_list.length; i++) { if (connection_list[i] === c) { wasm_close_connection(c.handle); connection_list.splice(i, 1); return; } } } export function send_on_connection( conn: wamr_connection, data: string, len: i32, ) { const data_buffer = string_to_arraybuffer(data); return wasm_send_on_connection(conn.handle, data_buffer, len); } export function config_connection(conn: wamr_connection, cfg: ArrayBuffer) { const cfg_len: i32 = attr_container_get_serialize_length(cfg); return wasm_config_connection(conn.handle, cfg, cfg_len); } // Wasmnizer-ts: @NativeSignature@ (i32, i32, i32)=>void // Wasmnizer-ts: @Export@ _on_connection_data export function on_connection_data(handle: i32, buffer: ArrayBuffer, len: i32) { for (let i = 0; i < connection_list.length; i++) { const conn = connection_list[i]; if (conn.handle === handle) { if (len === 0) { conn.on_event(conn, CONN_EVENT_TYPE_DISCONNECT, '', len); } else { const buffer_str = arraybuffer_to_string(buffer, len); conn.on_event(conn, CONN_EVENT_TYPE_DATA, buffer_str, len); } return; } } } ================================================ FILE: example/app-framework/lib/request.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import * as timer from './timer'; import { i32, arraybuffer_to_string, string_to_arraybuffer } from './utils'; // Wasmnizer-ts: @NativeSignature@ (i32, i32)=>boolean declare function wasm_response_send(buffer: ArrayBuffer, size: i32): boolean; // Wasmnizer-ts: @NativeSignature@ (i32)=>void declare function wasm_register_resource(url: ArrayBuffer): void; // Wasmnizer-ts: @NativeSignature@ (i32, i32)=>void declare function wasm_post_request(buffer: ArrayBuffer, size: i32): void; // Wasmnizer-ts: @NativeSignature@ (i32)=>void declare function wasm_sub_event(url: ArrayBuffer): void; const COAP_GET = 1; const COAP_POST = 2; const COAP_PUT = 3; const COAP_DELETE = 4; const COAP_EVENT = COAP_DELETE + 2; /* CoAP response codes */ export enum CoAP_Status { NO_ERROR = 0, CREATED_2_01 = 65 /* CREATED */, DELETED_2_02 = 66 /* DELETED */, VALID_2_03 = 67 /* NOT_MODIFIED */, CHANGED_2_04 = 68 /* CHANGED */, CONTENT_2_05 = 69 /* OK */, CONTINUE_2_31 = 95 /* CONTINUE */, BAD_REQUEST_4_00 = 128 /* BAD_REQUEST */, UNAUTHORIZED_4_01 = 129 /* UNAUTHORIZED */, BAD_OPTION_4_02 = 130 /* BAD_OPTION */, FORBIDDEN_4_03 = 131 /* FORBIDDEN */, NOT_FOUND_4_04 = 132 /* NOT_FOUND */, METHOD_NOT_ALLOWED_4_05 = 133 /* METHOD_NOT_ALLOWED */, NOT_ACCEPTABLE_4_06 = 134 /* NOT_ACCEPTABLE */, PRECONDITION_FAILED_4_12 = 140 /* BAD_REQUEST */, REQUEST_ENTITY_TOO_LARGE_4_13 = 141 /* REQUEST_ENTITY_TOO_LARGE */, UNSUPPORTED_MEDIA_TYPE_4_15 = 143 /* UNSUPPORTED_MEDIA_TYPE */, INTERNAL_SERVER_ERROR_5_00 = 160 /* INTERNAL_SERVER_ERROR */, NOT_IMPLEMENTED_5_01 = 161 /* NOT_IMPLEMENTED */, BAD_GATEWAY_5_02 = 162 /* BAD_GATEWAY */, SERVICE_UNAVAILABLE_5_03 = 163 /* SERVICE_UNAVAILABLE */, GATEWAY_TIMEOUT_5_04 = 164 /* GATEWAY_TIMEOUT */, PROXYING_NOT_SUPPORTED_5_05 = 165 /* PROXYING_NOT_SUPPORTED */, /* Erbium errors */ MEMORY_ALLOCATION_ERROR = 192, PACKET_SERIALIZATION_ERROR, /* Erbium hooks */ MANUAL_RESPONSE, PING_RESPONSE, } let g_mid: i32 = 0; let transaction_list = new Array(); const REQUEST_PACKET_FIX_PART_LEN = 18; const RESPONSE_PACKET_FIX_PART_LEN = 16; const TRANSACTION_TIMEOUT_MS = 5000; let g_trans_timer: timer.user_timer; const Reg_Event = 0; const Reg_Request = 1; class wamr_request { mid: i32 = 0; url = ''; action: i32 = 0; fmt: i32 = 0; payload: ArrayBuffer; payload_len: i32 = 0; sender: i32 = 0; constructor( mid: i32, url: string, action: i32, fmt: i32, payload: ArrayBuffer, payload_len: i32, ) { this.mid = mid; this.url = url; this.action = action; this.fmt = fmt; this.payload = payload; this.payload_len = payload_len; } } class wamr_response { mid: i32 = 0; status: i32 = 0; fmt: i32 = 0; payload: ArrayBuffer | null; payload_len: i32 = 0; receiver: i32 = 0; constructor( mid: i32, status: i32, fmt: i32, payload: ArrayBuffer | null, payload_len: i32, ) { this.mid = mid; this.status = status; this.fmt = fmt; this.payload = payload; this.payload_len = payload_len; } set_status(status: i32): void { this.status = status; } set_payload(payload: ArrayBuffer, payload_len: i32): void { this.payload = payload; this.payload_len = payload_len; } set_response( status: i32, fmt: i32, payload: ArrayBuffer | null, payload_len: i32, ) { this.status = status; this.fmt = fmt; this.payload = payload; this.payload_len = payload_len; } } class wamr_resource { url: string; type: number; cb: request_handler_f; constructor(url: string, type: number, cb: request_handler_f) { this.url = url; this.type = type; this.cb = cb; } } function is_expire( trans: wamr_transaction, index: i32, array: Array, ): boolean { const now = timer.now(); const elapsed_ms = now < trans.time ? now + (0xffffffff - trans.time) + 1 : now - trans.time; return elapsed_ms >= TRANSACTION_TIMEOUT_MS; } function not_expire( trans: wamr_transaction, index: i32, array: Array, ): boolean { const now = timer.now(); const elapsed_ms = now < trans.time ? now + (0xffffffff - trans.time) + 1 : now - trans.time; return elapsed_ms < TRANSACTION_TIMEOUT_MS; } function transaction_timeout_handler(my_timer: timer.user_timer): void { let now = timer.now(); const expired = transaction_list.filter(is_expire); transaction_list = transaction_list.filter(not_expire); expired.forEach((item) => { item.cb(null); transaction_remove(item); }); if (transaction_list.length > 0) { let elpased_ms: number; now = timer.now(); if (now < transaction_list[0].time) { elpased_ms = now + (0xffffffff - transaction_list[0].time) + 1; } else { elpased_ms = now - transaction_list[0].time; } const ms_to_expiry = TRANSACTION_TIMEOUT_MS - elpased_ms; timer.timer_restart(g_trans_timer, ms_to_expiry); } else { timer.timer_cancel(g_trans_timer); } } function transaction_find(mid: number): wamr_transaction | null { for (let i = 0; i < transaction_list.length; i++) { if (transaction_list[i].mid == mid) return transaction_list[i]; } return null; } function transaction_add(trans: wamr_transaction): void { transaction_list.push(trans); if (transaction_list.length == 1) { g_trans_timer = timer.setTimeout( transaction_timeout_handler, TRANSACTION_TIMEOUT_MS, ); } } function transaction_remove(trans: wamr_transaction): void { const index = transaction_list.indexOf(trans); transaction_list.splice(index, 1); } class wamr_transaction { mid: number; time: number; cb: (resp: wamr_response | null) => void; constructor( mid: number, time: number, cb: (resp: wamr_response | null) => void, ) { this.mid = mid; this.time = time; this.cb = cb; } } function pack_request(req: wamr_request): DataView { const url_len = req.url.length + 1; const len = REQUEST_PACKET_FIX_PART_LEN + url_len + req.payload_len; const buf = new ArrayBuffer(len); const dataview = new DataView(buf, 0, len); dataview.setUint8(0, 1); dataview.setUint8(1, req.action); dataview.setUint16(2, req.fmt); dataview.setUint32(4, req.mid); dataview.setUint32(8, req.sender); dataview.setUint16(12, url_len); dataview.setUint32(14, req.payload_len); let i = 0; for (i = 0; i < url_len - 1; i++) { dataview.setUint8(i + 18, req.url.charCodeAt(i)); } dataview.setUint8(i + 18, 0); const payload_view = new DataView(req.payload); for (i = 0; i < req.payload_len; i++) { dataview.setUint8(i + 18 + url_len, payload_view.getUint8(i)); } return dataview; } function unpack_request(packet: ArrayBuffer, size: i32): wamr_request { const dataview = new DataView(packet, 0, size); // if (dataview.getUint8(0) != 1) throw new Error('packet version mismatch'); // if (size < REQUEST_PACKET_FIX_PART_LEN) // throw new Error('packet size error'); const url_len = dataview.getUint16(12); const payload_len = dataview.getUint32(14); // if (size != REQUEST_PACKET_FIX_PART_LEN + url_len + payload_len) // throw new Error('packet size error'); const action = dataview.getUint8(1); const fmt = dataview.getUint16(2); const mid = dataview.getUint32(4); const sender = dataview.getUint32(8); const url = packet.slice( REQUEST_PACKET_FIX_PART_LEN, REQUEST_PACKET_FIX_PART_LEN + url_len, ); const url_string = arraybuffer_to_string(url, url_len); const payload = packet.slice( REQUEST_PACKET_FIX_PART_LEN + url_len, REQUEST_PACKET_FIX_PART_LEN + url_len + payload_len, ); const req = new wamr_request( mid, url_string, action, fmt, payload, payload_len, ); req.sender = sender; return req; } function pack_response(resp: wamr_response): DataView { const len = RESPONSE_PACKET_FIX_PART_LEN + resp.payload_len; const buf = new ArrayBuffer(len); const dataview = new DataView(buf, 0, len); dataview.setUint8(0, 1); dataview.setUint8(1, resp.status); dataview.setUint16(2, resp.fmt); dataview.setUint32(4, resp.mid); dataview.setUint32(8, resp.receiver); dataview.setUint32(12, resp.payload_len); if (resp.payload != null) { const payload_view = new DataView(resp.payload!); for (let i = 0; i < resp.payload_len; i++) { dataview.setUint8(i + 16, payload_view.getUint8(i)); } } return dataview; } function unpack_response(packet: ArrayBuffer, size: i32): wamr_response { const dataview = new DataView(packet, 0, size); // if (dataview.getUint8(0) != 1) throw new Error('packet version mismatch'); // if (size < RESPONSE_PACKET_FIX_PART_LEN) // throw new Error('packet size error'); const payload_len = dataview.getUint32(12); // if (size != RESPONSE_PACKET_FIX_PART_LEN + payload_len) // throw new Error('packet size error'); const status = dataview.getUint8(1); const fmt = dataview.getUint16(2); const mid = dataview.getUint32(4); const receiver = dataview.getUint32(8); const payload = packet.slice(RESPONSE_PACKET_FIX_PART_LEN); const resp = new wamr_response(mid, status, fmt, payload, payload_len); resp.receiver = receiver; return resp; } function do_request( req: wamr_request, cb: (resp: wamr_response | null) => void, ): void { const trans = new wamr_transaction(req.mid, timer.now(), cb); const msg = pack_request(req); transaction_add(trans); wasm_post_request(msg.buffer, msg.byteLength); } function do_response(resp: wamr_response): void { const msg = pack_response(resp); wasm_response_send(msg.buffer, msg.byteLength); } const resource_list = new Array(); type request_handler_f = (req: wamr_request) => void; function register_url_handler( url: string, cb: request_handler_f, type: number, ): void { for (let i = 0; i < resource_list.length; i++) { if (resource_list[i].type == type && resource_list[i].url == url) { resource_list[i].cb = cb; return; } } const res = new wamr_resource(url, type, cb); resource_list.push(res); const url_buffer = string_to_arraybuffer(url); if (type == Reg_Request) wasm_register_resource(url_buffer); else wasm_sub_event(url_buffer); } function is_event_type(req: wamr_request): boolean { return req.action == COAP_EVENT; } function check_url_start(url: string, leading_str: string): boolean { return url.split('/')[0] == leading_str.split('/')[0]; } /* User APIs below */ export function post( url: string, payload: ArrayBuffer, payload_len: number, tag: string, cb: (resp: wamr_response | null) => void, ): void { const req = new wamr_request( g_mid, url, COAP_POST, 0, payload, payload_len, ); g_mid = g_mid + 1; do_request(req, cb); } export function get( url: string, tag: string, cb: (resp: wamr_response | null) => void, ): void { const req = new wamr_request( g_mid, url, COAP_GET, 0, new ArrayBuffer(0), 0, ); g_mid = g_mid + 1; do_request(req, cb); } export function put( url: string, payload: ArrayBuffer, payload_len: number, tag: string, cb: (resp: wamr_response | null) => void, ): void { const req = new wamr_request(g_mid, url, COAP_PUT, 0, payload, payload_len); g_mid = g_mid + 1; do_request(req, cb); } export function del( url: string, tag: string, cb: (resp: wamr_response | null) => void, ): void { const req = new wamr_request( g_mid, url, COAP_DELETE, 0, new ArrayBuffer(0), 0, ); g_mid = g_mid + 1; do_request(req, cb); } export function make_response_for_request(req: wamr_request): wamr_response { const resp = new wamr_response( req.mid, CoAP_Status.CONTENT_2_05, 0, null, 0, ); resp.receiver = req.sender; return resp; } export function api_response_send(resp: wamr_response): void { do_response(resp); } export function register_resource_handler( url: string, request_handle: request_handler_f, ): void { register_url_handler(url, request_handle, Reg_Request); } export function publish_event( url: string, fmt: number, payload: ArrayBuffer, payload_len: number, ): void { const req = new wamr_request( g_mid, url, COAP_EVENT, fmt, payload, payload_len, ); g_mid = g_mid + 1; const msg = pack_request(req); wasm_post_request(msg.buffer, msg.byteLength); } export function subscribe_event(url: string, cb: request_handler_f): void { register_url_handler(url, cb, Reg_Event); } // Wasmnizer-ts: @NativeSignature@ (i32, i32)=>void // Wasmnizer-ts: @Export@ _on_request export function on_request(buffer: ArrayBuffer, size: i32): void { const req = unpack_request(buffer, size); const is_event = is_event_type(req); for (let i = 0; i < resource_list.length; i++) { if ( (is_event && resource_list[i].type == Reg_Event) || (!is_event && resource_list[i].type == Reg_Request) ) { if (check_url_start(req.url, resource_list[i].url)) { resource_list[i].cb(req); return; } } } console.log('on_request: exit. no service handler.'); } // Wasmnizer-ts: @NativeSignature@ (i32, i32)=>void // Wasmnizer-ts: @Export@ _on_response export function on_response(buffer: ArrayBuffer, size: i32): void { const resp = unpack_response(buffer, size); const trans = transaction_find(resp.mid); if (trans != null) { if (transaction_list.indexOf(trans) == 0) { if (transaction_list.length >= 2) { let elpased_ms: number; const now = timer.now(); if (now < transaction_list[1].time) { elpased_ms = now + (0xffffffff - transaction_list[1].time) + 1; } else { elpased_ms = now - transaction_list[1].time; } const ms_to_expiry = TRANSACTION_TIMEOUT_MS - elpased_ms; timer.timer_restart(g_trans_timer, ms_to_expiry); } else { timer.timer_cancel(g_trans_timer); } } trans.cb(resp); } } ================================================ FILE: example/app-framework/lib/timer.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { i32 } from './utils'; declare function wasm_create_timer(a: i32, b: boolean, c: boolean): i32; declare function wasm_timer_cancel(a: i32): void; declare function wasm_timer_restart(a: i32, b: i32): void; declare function wasm_get_sys_tick_ms(): i32; export const timer_list = new Array(); export class user_timer { timer_id: i32 = 0; timeout: i32; period = false; cb: (my_timer: user_timer) => void; constructor( cb: (my_timer: user_timer) => void, timeout: i32, period: boolean, ) { this.cb = cb; this.timeout = timeout; this.period = period; this.timer_id = timer_create(this.timeout, this.period, true); } } export function timer_create(a: i32, b: boolean, c: boolean): i32 { return wasm_create_timer(a, b, c); } export function setTimeout( cb: (my_timer: user_timer) => void, timeout: i32, ): user_timer { const timer = new user_timer(cb, timeout, false); timer_list.push(timer); return timer; } export function setInterval( cb: (my_timer: user_timer) => void, timeout: i32, ): user_timer { const timer = new user_timer(cb, timeout, true); timer_list.push(timer); return timer; } export function timer_cancel(timer: user_timer): void { wasm_timer_cancel(timer.timer_id); let i = 0; for (i = 0; i < timer_list.length; i++) { if (timer_list[i].timer_id == timer.timer_id) break; } timer_list.splice(i, 1); } export function timer_restart(timer: user_timer, interval: number): void { wasm_timer_restart(timer.timer_id, interval); } export function now(): i32 { return wasm_get_sys_tick_ms(); } // Wasmnizer-ts: @Export@ _on_timer_callback export function on_timer_callback(on_timer_id: i32): void { for (let i = 0; i < timer_list.length; i++) { const t = timer_list[i]; if (t.timer_id == on_timer_id) { t.cb(t); } } } ================================================ FILE: example/app-framework/lib/utils.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export type i32 = number; export function arraybuffer_to_string( buffer: ArrayBuffer, buffer_length: number, ) { const codes: number[] = new Array(buffer_length); const dataview = new DataView(buffer); for (let i = 0; i < buffer_length; i++) { codes[i] = dataview.getUint8(i); } return String.fromCharCode(...codes); } export function string_to_arraybuffer(url: string) { const url_length = url.length; const arraybuffer = new ArrayBuffer(url_length); const dataview = new DataView(arraybuffer); for (let i = 0; i < url_length; i++) { dataview.setUint8(i, url.charCodeAt(i)); } return arraybuffer; } ================================================ FILE: example/app-framework/profiles/host-aot/wamr_config_wasmnizer_ts.cmake ================================================ # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception set (WAMR_BUILD_PLATFORM "linux") set (WAMR_BUILD_TARGET X86_64) set (WAMR_BUILD_INTERP 1) set (WAMR_BUILD_AOT 1) set (WAMR_BUILD_JIT 0) set (WAMR_BUILD_SIMD 1) set (WAMR_BUILD_LIBC_BUILTIN 1) set (WAMR_BUILD_LIBC_WASI 0) set (WAMR_BUILD_APP_FRAMEWORK 1) set (WAMR_BUILD_APP_LIST WAMR_APP_BUILD_BASE WAMR_APP_BUILD_CONNECTION WAMR_APP_BUILD_SENSOR) set (WAMR_BUILD_GC 1) set (WAMR_BUILD_GC_BINARYEN 1) set (WAMR_BUILD_STRINGREF 1) set (USE_SIMPLE_LIBDYNTYPE 1) set (RUNTIMR_DIR ${CMAKE_CURRENT_LIST_DIR}/../../../../runtime-library) ## stringref set(STRINGREF_DIR ${RUNTIMR_DIR}/stringref) set(WAMR_STRINGREF_IMPL_SOURCE ${STRINGREF_DIR}/stringref_simple.c ) ## quickjs set(QUICKJS_SRC_DIR ${RUNTIMR_DIR}/deps/quickjs) include_directories(${QUICKJS_SRC_DIR}) ## libdyntype set(LIBDYNTYPE_DIR ${RUNTIMR_DIR}/libdyntype) include (${LIBDYNTYPE_DIR}/libdyntype.cmake) ================================================ FILE: example/app-framework/profiles/host-interp/wamr_config_wasmnizer_ts.cmake ================================================ # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception set (WAMR_BUILD_PLATFORM "linux") set (WAMR_BUILD_TARGET X86_64) set (WAMR_BUILD_INTERP 1) set (WAMR_BUILD_AOT 1) set (WAMR_BUILD_JIT 0) set (WAMR_BUILD_LIBC_BUILTIN 1) set (WAMR_BUILD_LIBC_WASI 0) set (WAMR_BUILD_APP_FRAMEWORK 1) set (WAMR_BUILD_APP_LIST WAMR_APP_BUILD_BASE WAMR_APP_BUILD_CONNECTION WAMR_APP_BUILD_SENSOR) set (WAMR_BUILD_GC 1) set (WAMR_BUILD_GC_BINARYEN 1) set (WAMR_BUILD_STRINGREF 1) set (USE_SIMPLE_LIBDYNTYPE 1) set (RUNTIMR_DIR ${CMAKE_CURRENT_LIST_DIR}/../../../../runtime-library) ## stringref set(STRINGREF_DIR ${RUNTIMR_DIR}/stringref) set(WAMR_STRINGREF_IMPL_SOURCE ${STRINGREF_DIR}/stringref_simple.c ) ## quickjs set(QUICKJS_SRC_DIR ${RUNTIMR_DIR}/deps/quickjs) include_directories(${QUICKJS_SRC_DIR}) ## libdyntype set(LIBDYNTYPE_DIR ${RUNTIMR_DIR}/libdyntype) include (${LIBDYNTYPE_DIR}/libdyntype.cmake) ================================================ FILE: example/app-framework/src/iwasm_main.c ================================================ /* * Copyright (C) 2019 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #ifndef CONNECTION_UART #include #include #include #include #else #include #endif #include #include #include #include #include #include #include #include #include #include #include #include "runtime_lib.h" #include "runtime_timer.h" #include "native_interface.h" #include "app_manager_export.h" #include "bh_platform.h" #include "runtime_sensor.h" #include "bi-inc/attr_container.h" #include "module_wasm_app.h" #include "wasm_export.h" #include "libdyntype_export.h" #define MAX 2048 #ifndef CONNECTION_UART #define SA struct sockaddr static char *host_address = "127.0.0.1"; static int port = 8888; #else static char *uart_device = "/dev/ttyS2"; static int baudrate = B115200; #endif extern bool init_sensor_framework(); extern void exit_sensor_framework(); extern void exit_connection_framework(); extern int aee_host_msg_callback(void *msg, uint32_t msg_len); extern bool init_connection_framework(); extern uint32_t get_libdyntype_symbols(char **p_module_name, NativeSymbol **p_native_symbols); extern uint32_t get_lib_console_symbols(char **p_module_name, NativeSymbol **p_native_symbols); extern uint32_t get_lib_array_symbols(char **p_module_name, NativeSymbol **p_native_symbols); extern uint32_t get_lib_timer_symbols(char **p_module_name, NativeSymbol **p_native_symbols); extern uint32_t get_struct_indirect_symbols(char **p_module_name, NativeSymbol **p_native_symbols); extern dyn_value_t dyntype_callback_wasm_dispatcher(void *exec_env_v, dyn_ctx_t ctx, void *vfunc, dyn_value_t this_obj, int argc, dyn_value_t *args); #ifndef CONNECTION_UART int listenfd = -1; int sockfd = -1; static pthread_mutex_t sock_lock = PTHREAD_MUTEX_INITIALIZER; #else int uartfd = -1; #endif #ifndef CONNECTION_UART static bool server_mode = false; // Function designed for chat between client and server. void * func(void *arg) { char buff[MAX]; int n; struct sockaddr_in servaddr; while (1) { if (sockfd != -1) close(sockfd); // socket create and verification sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { printf("socket creation failed...\n"); return NULL; } else printf("Socket successfully created..\n"); bzero(&servaddr, sizeof(servaddr)); // assign IP, PORT servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = inet_addr(host_address); servaddr.sin_port = htons(port); // connect the client socket to server socket if (connect(sockfd, (SA *)&servaddr, sizeof(servaddr)) != 0) { printf("connection with the server failed...\n"); sleep(10); continue; } else { printf("connected to the server..\n"); } // infinite loop for chat for (;;) { bzero(buff, MAX); // read the message from client and copy it in buffer n = read(sockfd, buff, sizeof(buff)); // print buffer which contains the client contents // fprintf(stderr, "recieved %d bytes from host: %s", n, buff); // socket disconnected if (n <= 0) break; aee_host_msg_callback(buff, n); } } // After chatting close the socket close(sockfd); } static bool host_init() { return true; } int host_send(void *ctx, const char *buf, int size) { int ret; if (pthread_mutex_trylock(&sock_lock) == 0) { if (sockfd == -1) { pthread_mutex_unlock(&sock_lock); return 0; } ret = write(sockfd, buf, size); pthread_mutex_unlock(&sock_lock); return ret; } return -1; } void host_destroy() { if (server_mode) close(listenfd); pthread_mutex_lock(&sock_lock); close(sockfd); pthread_mutex_unlock(&sock_lock); } /* clang-format off */ host_interface interface = { .init = host_init, .send = host_send, .destroy = host_destroy }; /* clang-format on */ /* Change it to 1 when fuzzing test */ #define WASM_ENABLE_FUZZ_TEST 0 void * func_server_mode(void *arg) { int clilent; struct sockaddr_in serv_addr, cli_addr; int n; char buff[MAX]; struct sigaction sa; sa.sa_handler = SIG_IGN; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sigaction(SIGPIPE, &sa, 0); /* First call to socket() function */ listenfd = socket(AF_INET, SOCK_STREAM, 0); if (listenfd < 0) { perror("ERROR opening socket"); exit(1); } /* Initialize socket structure */ bzero((char *)&serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(port); /* Now bind the host address using bind() call.*/ if (bind(listenfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { perror("ERROR on binding"); exit(1); } listen(listenfd, 5); clilent = sizeof(cli_addr); while (1) { pthread_mutex_lock(&sock_lock); sockfd = accept(listenfd, (struct sockaddr *)&cli_addr, &clilent); pthread_mutex_unlock(&sock_lock); if (sockfd < 0) { perror("ERROR on accept"); exit(1); } printf("connection established!\n"); for (;;) { bzero(buff, MAX); // read the message from client and copy it in buffer n = read(sockfd, buff, sizeof(buff)); // socket disconnected if (n <= 0) { pthread_mutex_lock(&sock_lock); close(sockfd); sockfd = -1; pthread_mutex_unlock(&sock_lock); sleep(1); break; } aee_host_msg_callback(buff, n); } #if WASM_ENABLE_FUZZ_TEST != 0 /* Exit the process when host disconnect. This is helpful for reproducing failure case. */ close(sockfd); exit(1); #endif } } #else static int parse_baudrate(int baud) { switch (baud) { case 9600: return B9600; case 19200: return B19200; case 38400: return B38400; case 57600: return B57600; case 115200: return B115200; case 230400: return B230400; case 460800: return B460800; case 500000: return B500000; case 576000: return B576000; case 921600: return B921600; case 1000000: return B1000000; case 1152000: return B1152000; case 1500000: return B1500000; case 2000000: return B2000000; case 2500000: return B2500000; case 3000000: return B3000000; case 3500000: return B3500000; case 4000000: return B4000000; default: return -1; } } static bool uart_init(const char *device, int baudrate, int *fd) { int uart_fd; struct termios uart_term; uart_fd = open(device, O_RDWR | O_NOCTTY); if (uart_fd <= 0) return false; memset(&uart_term, 0, sizeof(uart_term)); uart_term.c_cflag = baudrate | CS8 | CLOCAL | CREAD; uart_term.c_iflag = IGNPAR; uart_term.c_oflag = 0; /* set noncanonical mode */ uart_term.c_lflag = 0; uart_term.c_cc[VTIME] = 30; uart_term.c_cc[VMIN] = 1; tcflush(uart_fd, TCIFLUSH); if (tcsetattr(uart_fd, TCSANOW, &uart_term) != 0) { close(uart_fd); return false; } *fd = uart_fd; return true; } static void * func_uart_mode(void *arg) { int n; char buff[MAX]; if (!uart_init(uart_device, baudrate, &uartfd)) { printf("open uart fail! %s\n", uart_device); return NULL; } for (;;) { bzero(buff, MAX); n = read(uartfd, buff, sizeof(buff)); if (n <= 0) { close(uartfd); uartfd = -1; break; } aee_host_msg_callback(buff, n); } return NULL; } static int uart_send(void *ctx, const char *buf, int size) { int ret; ret = write(uartfd, buf, size); return ret; } static void uart_destroy() { close(uartfd); } /* clang-format off */ static host_interface interface = { .send = uart_send, .destroy = uart_destroy }; /* clang-format on */ #endif static attr_container_t * read_test_sensor(void *sensor) { attr_container_t *attr_obj = attr_container_create("read test sensor data"); if (attr_obj) { bool ret = attr_container_set_string(&attr_obj, "name", "read test sensor"); if (!ret) { attr_container_destroy(attr_obj); return NULL; } return attr_obj; } return NULL; } static bool config_test_sensor(void *s, void *config) { return false; } // static char global_heap_buf[1024 * 1024] = { 0 }; /* clang-format off */ static void showUsage() { #ifndef CONNECTION_UART printf("Usage:\n"); printf("\nWork as TCP server mode:\n"); printf("\tsimple -s|--server_mode -p|--port \n"); printf("where\n"); printf("\t represents the port that would be listened on and the default is 8888\n"); printf("\nWork as TCP client mode:\n"); printf("\tsimple -a|--host_address -p|--port \n"); printf("where\n"); printf("\t represents the network address of host and the default is 127.0.0.1\n"); printf("\t represents the listen port of host and the default is 8888\n"); #else printf("Usage:\n"); printf("\tsimple -u -b \n\n"); printf("where\n"); printf("\t represents the UART device name and the default is /dev/ttyS2\n"); printf("\t represents the UART device baudrate and the default is 115200\n"); #endif } /* clang-format on */ static bool parse_args(int argc, char *argv[]) { int c; while (1) { int optIndex = 0; static struct option longOpts[] = { #ifndef CONNECTION_UART { "server_mode", no_argument, NULL, 's' }, { "host_address", required_argument, NULL, 'a' }, { "port", required_argument, NULL, 'p' }, #else { "uart", required_argument, NULL, 'u' }, { "baudrate", required_argument, NULL, 'b' }, #endif { "help", required_argument, NULL, 'h' }, { 0, 0, 0, 0 } }; c = getopt_long(argc, argv, "sa:p:u:b:w:h", longOpts, &optIndex); if (c == -1) break; switch (c) { #ifndef CONNECTION_UART case 's': server_mode = true; break; case 'a': host_address = optarg; printf("host address: %s\n", host_address); break; case 'p': port = atoi(optarg); printf("port: %d\n", port); break; #else case 'u': uart_device = optarg; printf("uart device: %s\n", uart_device); break; case 'b': baudrate = parse_baudrate(atoi(optarg)); printf("uart baudrate: %s\n", optarg); break; #endif case 'h': showUsage(); return false; default: showUsage(); return false; } } return true; } // Driver function int iwasm_main(int argc, char *argv[]) { RuntimeInitArgs init_args; korp_tid tid; if (!parse_args(argc, argv)) return -1; memset(&init_args, 0, sizeof(RuntimeInitArgs)); #if USE_GLOBAL_HEAP_BUF != 0 init_args.mem_alloc_type = Alloc_With_Pool; init_args.mem_alloc_option.pool.heap_buf = global_heap_buf; init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); #else init_args.mem_alloc_type = Alloc_With_Allocator; init_args.mem_alloc_option.allocator.malloc_func = malloc; init_args.mem_alloc_option.allocator.realloc_func = realloc; init_args.mem_alloc_option.allocator.free_func = free; #endif /* initialize runtime environment */ if (!wasm_runtime_full_init(&init_args)) { printf("Init runtime environment failed.\n"); return -1; } /* initialize dyntype context and set callback dispatcher */ dyn_ctx_t dyn_ctx = dyntype_context_init(); dyntype_set_callback_dispatcher(dyntype_callback_wasm_dispatcher); /* Register APIs required by ts2wasm */ NativeSymbol *native_symbols; char *module_name; uint32_t symbol_count; symbol_count = get_libdyntype_symbols(&module_name, &native_symbols); if (!wasm_runtime_register_natives(module_name, native_symbols, symbol_count)) { printf("Register libdyntype APIs failed.\n"); goto fail1; } symbol_count = get_lib_console_symbols(&module_name, &native_symbols); if (!wasm_runtime_register_natives(module_name, native_symbols, symbol_count)) { printf("Register stdlib APIs failed.\n"); goto fail1; } symbol_count = get_lib_array_symbols(&module_name, &native_symbols); if (!wasm_runtime_register_natives(module_name, native_symbols, symbol_count)) { printf("Register stdlib APIs failed.\n"); goto fail1; } symbol_count = get_lib_timer_symbols(&module_name, &native_symbols); if (!wasm_runtime_register_natives(module_name, native_symbols, symbol_count)) { printf("Register stdlib APIs failed.\n"); goto fail1; } symbol_count = get_struct_indirect_symbols(&module_name, &native_symbols); if (!wasm_runtime_register_natives(module_name, native_symbols, symbol_count)) { printf("Register struct-dyn APIs failed.\n"); goto fail1; } /* connection framework */ if (!init_connection_framework()) { goto fail1; } /* sensor framework */ if (!init_sensor_framework()) { goto fail2; } /* timer manager */ if (!init_wasm_timer()) { goto fail3; } /* add the sys sensor objects */ add_sys_sensor("sensor_test1", "This is a sensor for test", 0, 1000, read_test_sensor, config_test_sensor); add_sys_sensor("sensor_test2", "This is a sensor for test", 0, 1000, read_test_sensor, config_test_sensor); start_sensor_framework(); #ifndef CONNECTION_UART if (server_mode) os_thread_create(&tid, func_server_mode, NULL, BH_APPLET_PRESERVED_STACK_SIZE); else os_thread_create(&tid, func, NULL, BH_APPLET_PRESERVED_STACK_SIZE); #else os_thread_create(&tid, func_uart_mode, NULL, BH_APPLET_PRESERVED_STACK_SIZE); #endif app_manager_startup(&interface); exit_wasm_timer(); fail3: exit_sensor_framework(); fail2: exit_connection_framework(); fail1: /* destroy dynamic ctx */ dyntype_context_destroy(dyn_ctx); wasm_runtime_destroy(); return -1; } ================================================ FILE: example/app-framework/src/main.c ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ extern void iwasm_main(); int main(int argc, char *argv[]) { iwasm_main(argc, argv); return 0; } ================================================ FILE: lib/builtin/builtin_name.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { Type, TypeKind } from '../../src/type'; export namespace BuiltinNames { // wasm global variable export const dataEnd = '__data_end'; export const stackPointer = '__stack_pointer'; export const heapBase = '__heap_base'; // wasm table export const extrefTable = 'extref_table'; // wasm default variable export const byteSize = 32; export const stackSize = 1024; export const memInitialPages = 1; export const memMaximumPages = 10; export const tableInitialPages = 1; export const tableMaximumPages = 10; export const tableGrowDelta = 10; export const memoryReserveOffset = 0; export const memoryReserveMaxSize = 100; // wasm function export const start = '~start'; export const globalInitFunc = 'global_init'; // delimiters export const moduleDelimiter = '|'; export const declareSuffix = '-declare'; export const wrapperSuffix = '-wrapper'; // import external name export const externalModuleName = 'env'; // builtin module name export const builtinModuleName = 'builtin'; // customize wasm util functions prefix name export const utilsFuncName = 'utils'; // builtin libc functions export const mallocFunc = 'malloc'; export const freeFunc = 'free'; // builtin file name export const builtinTypeName = 'lib.type.d.ts'; export const builtinImplementFileName = 'lib_builtin.ts'; // export const builtinFileNames = ['lib.type.d.ts', builtinImplementFileName]; // export const builtinFileNames = ['lib.type.d.ts']; export const builtinFileNames = [builtinTypeName, builtinImplementFileName]; // builtin function name export const globalInitFuncName = 'global|init|func'; export const findPropertyFlagAndIndex = 'find_property_flag_and_index'; export const findPropertyType = 'find_property_type'; export const getInfcProperty = 'get_infc_property'; export const getTupleField = 'get_tuple_field'; // builtin globals export const builtinTypeManglePrefix = 'lib/builtin/lib.type.d'; export const nanName = `${builtinTypeManglePrefix}|NaN`; export const infinityName = `${builtinTypeManglePrefix}|Infinity`; export const builtinGlobalNames = [nanName, infinityName]; // default envParamLen export const envParamLen = 2; // Globals that should be fallbacked to libdyntype export const jsonName = `${builtinTypeManglePrefix}|JSON`; export const promiseName = `${builtinTypeManglePrefix}|Promise`; export const dateName = `${builtinTypeManglePrefix}|Date`; export const fallbackGlobalNames = [jsonName, promiseName, dateName]; // builtin class name export const MATH = 'Math'; export const ARRAY = 'Array'; export const STRING = 'String'; export const NUMBER = 'Number'; export const BOOLEAN = 'Boolean'; export const OBJECT = 'Object'; export const FUNCTION = 'Function'; export const CONSOLE = 'console'; export const PROMISE = 'Promise'; export const MAP = 'Map'; export const SET = 'Set'; export const OBJECTCONSTRUCTOR = 'ObjectConstructor'; export const FUNCTIONCONSTRCTOR = 'FunctionConstructor'; export const ARRAYBUFFER = 'ArrayBuffer'; export const ARRAYBUFFERCONSTRCTOR = 'ArrayBufferConstructor'; export const DATAVIEW = 'DataView'; export const STRINGCONSTRCTOR = 'StringConstructor'; // decorator name export const decorator = 'binaryen'; // decorator function name export const mathSqrtFuncName = 'Math|sqrt'; export const mathAbsFuncName = 'Math|abs'; export const mathCeilFuncName = 'Math|ceil'; export const mathFloorFuncName = 'Math|floor'; export const mathTruncFuncName = 'Math|trunc'; export const arrayIsArrayFuncName = 'ArrayConstructor|isArray'; export const stringConcatFuncName = 'String|concat'; export const stringSliceFuncName = 'String|slice'; export const stringEQFuncName = 'string_eq'; export const stringReplaceFuncName = 'String|replace'; export const stringSubStringFuncName = 'String|substring'; export const stringCharCodeAtFuncName = 'String|charCodeAt'; export const stringSplitFuncName = 'String|split'; export const stringIndexOfFuncName = 'String|indexOf'; export const stringLastIndexOfFuncName = 'String|lastIndexOf'; export const stringIndexOfInternalFuncName = 'String|indexOfInternal'; export const stringMatchFuncName = 'String|match'; export const stringSearchFuncName = 'String|search'; export const stringcharAtFuncName = 'String|charAt'; export const stringtoLowerCaseFuncName = 'String|toLowerCase'; export const stringtoUpperCaseFuncName = 'String|toUpperCase'; export const stringtrimFuncName = 'String|trim'; export const anyrefCond = 'anyrefCond'; export const newExtRef = 'newExtRef'; export const allocExtRefTableSlot = 'allocExtRefTableSlot'; export const extRefTableMaskArr = 'extRefTableMaskArr'; export const getPropertyIfTypeIdMismatch = 'get_property_if_typeid_mismatch'; export const setPropertyIfTypeIdMismatch = 'set_property_if_typeid_mismatch'; export const errorTag = 'error'; export const finallyTag = 'finally'; export interface GenericFuncName { generic: string; f64: string; i64: string; f32: string; i32: string; anyref: string; } const createGenericFuncNames = ( class_name: string, method_name: string, ) => { return { generic: `${class_name}|${method_name}`, f64: `${class_name}|${method_name}_f64`, i64: `${class_name}|${method_name}_i64`, f32: `${class_name}|${method_name}_f32`, i32: `${class_name}|${method_name}_i32`, anyref: `${class_name}|${method_name}_anyref`, }; }; // builtin instance function name export const stringLengthFuncName = 'String|length'; export const arrayLengthFuncName = 'Array|length'; export const arrayPushFuncNames = createGenericFuncNames('Array', 'push'); export const arrayPopFuncNames = createGenericFuncNames('Array', 'pop'); export const arrayJoinFuncNames = createGenericFuncNames('Array', 'join'); export const arrayConcatFuncNames = createGenericFuncNames( 'Array', 'concat', ); export const arrayReverseFuncNames = createGenericFuncNames( 'Array', 'reverse', ); export const arrayShiftFuncNames = createGenericFuncNames('Array', 'shift'); export const arraySliceFuncNames = createGenericFuncNames('Array', 'slice'); export const arraySortFuncNames = createGenericFuncNames('Array', 'sort'); export const arraySpliceFuncNames = createGenericFuncNames( 'Array', 'splice', ); export const arrayUnshiftFuncNames = createGenericFuncNames( 'Array', 'unshift', ); export const arrayIndexOfFuncNames = createGenericFuncNames( 'Array', 'indexOf', ); export const arrayLastIndexOfFuncNames = createGenericFuncNames( 'Array', 'lastIndexOf', ); export const arrayEveryFuncNames = createGenericFuncNames('Array', 'every'); export const arraySomeFuncNames = createGenericFuncNames('Array', 'some'); export const arrayForEachFuncNames = createGenericFuncNames( 'Array', 'forEach', ); export const arrayMapFuncNames = createGenericFuncNames('Array', 'map'); export const arrayFilterFuncNames = createGenericFuncNames( 'Array', 'filter', ); export const arrayReduceFuncNames = createGenericFuncNames( 'Array', 'reduce', ); export const arrayReduceRightFuncNames = createGenericFuncNames( 'Array', 'reduceRight', ); export const arrayFindFuncNames = createGenericFuncNames('Array', 'find'); export const arrayFindIndexFuncNames = createGenericFuncNames( 'Array', 'findIndex', ); export const arrayFillFuncNames = createGenericFuncNames('Array', 'fill'); export const arrayCopyWithinFuncNames = createGenericFuncNames( 'Array', 'copyWithin', ); export const arrayIncludesFuncNames = createGenericFuncNames( 'Array', 'includes', ); // export let genericBuiltinMethods : string[] = []; export const genericBuiltinMethods = [ `${builtinModuleName}|Array|push`, `${builtinModuleName}|Array|pop`, `${builtinModuleName}|Array|join`, `${builtinModuleName}|Array|concat`, `${builtinModuleName}|Array|reverse`, `${builtinModuleName}|Array|shift`, `${builtinModuleName}|Array|slice`, `${builtinModuleName}|Array|sort`, `${builtinModuleName}|Array|splice`, `${builtinModuleName}|Array|unshift`, `${builtinModuleName}|Array|indexOf`, `${builtinModuleName}|Array|lastIndexOf`, `${builtinModuleName}|Array|every`, `${builtinModuleName}|Array|some`, `${builtinModuleName}|Array|forEach`, `${builtinModuleName}|Array|map`, `${builtinModuleName}|Array|filter`, `${builtinModuleName}|Array|reduce`, `${builtinModuleName}|Array|reduceRight`, `${builtinModuleName}|Array|find`, `${builtinModuleName}|Array|findIndex`, `${builtinModuleName}|Array|fill`, `${builtinModuleName}|Array|copyWithin`, `${builtinModuleName}|Array|includes`, ]; export const JSGlobalObjects: Set = new Set(); export const fallbackConstructors = [ 'Map', 'Set', 'Promise', 'Date', 'Error', ]; export const builtInObjectTypes = [ 'ArrayBuffer', 'DataView', 'ArrayBufferConstructor', 'Math', 'Console', 'Array', 'ArrayConstructor', 'StringConstructor', ]; export function getSpecializedFuncName( mangledName: string, type: TypeKind, ): string { switch (type) { case TypeKind.NUMBER: case TypeKind.WASM_F64: return mangledName + '_f64'; case TypeKind.WASM_F32: return mangledName + '_f32'; case TypeKind.BOOLEAN: case TypeKind.WASM_I32: return mangledName + '_i32'; case TypeKind.WASM_I64: return mangledName + '_i64'; default: return mangledName + '_anyref'; } } export const ObjectToStringMethod = 'toString'; export const ObjectBuiltinMethods = [ObjectToStringMethod]; export const getPropNamesByMeta = 'get_prop_name_by_meta'; /* builtin constructor name */ export const ctorName = 'Constructor'; } ================================================ FILE: lib/builtin/lib.type.d.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ type i32 = number; type i64 = number; type f32 = number; type f64 = number; type anyref = any; interface SymbolConstructor { readonly iterator: unique symbol; } declare var Symbol: SymbolConstructor; interface Iterable { [Symbol.iterator](): Iterator; } interface IterableIterator extends Iterator { [Symbol.iterator](): IterableIterator; } interface ConcatArray { readonly length: number; readonly [n: number]: T; join(separator?: string): string; slice(start?: number, end?: number): T[]; } interface ArrayConstructor { new (arrayLength?: number): any[]; new (arrayLength: number): T[]; new (...items: T[]): T[]; (arrayLength?: number): any[]; (arrayLength: number): T[]; (...items: T[]): T[]; isArray(arg: any): arg is any[]; } declare var Array: ArrayConstructor; interface Array { length: number; push(...items: T[]): number; pop(): T; concat(...items: T[]): T[]; join(separator?: string): string; reverse(): T[]; shift(): T; slice(start?: number, end?: number): T[]; sort(compareFn: (a: T, b: T) => number): T[]; // change compareFn to required parameter splice(start: number, deleteCount?: number, ...items: T[]): T[]; unshift(...items: T[]): number; indexOf(searchElement: T, fromIndex?: number): number; lastIndexOf(searchElement: T, fromIndex?: number): number; every(predicate: (value: T, index: number, array: T[]) => boolean): boolean; some(predicate: (value: T, index: number, array: T[]) => boolean): boolean; forEach(callbackfn: (value: T, index: number, array: T[]) => void): void; map(callbackfn: (value: T, index: number, array: T[]) => U): U[]; filter(predicate: (value: T, index: number, array: T[]) => boolean): T[]; reduce( callbackfn: ( previousValue: T, currentValue: T, currentIndex: number, array: T[], ) => T, initialValue: T, ): T; reduceRight( callbackfn: ( previousValue: T, currentValue: T, currentIndex: number, array: T[], ) => T, initialValue: T, ): T; find(predicate: (value: T, index: number, obj: T[]) => boolean): any; findIndex( predicate: (value: T, index: number, obj: T[]) => boolean, ): number; fill(value: T, start?: number, end?: number): T[]; copyWithin(target: number, start: number, end?: number): T[]; includes(searchElement: T, fromIndex?: number): boolean; [n: number]: T; [Symbol.iterator](): IterableIterator; } interface Boolean {} interface Number {} interface Function {} type CallableFunction = Function; interface IArguments { [index: number]: any; } type NewableFunction = Function; interface Object {} interface RegExp {} interface String { readonly length: number; concat(...strings: string[]): string; slice(start?: number, end?: number): string; readonly [index: number]: string; replace(from: string, to: string): string; split(sep: string): string[]; indexOf(str: string): number; lastIndexOf(str: string): number; match(pattern: string): string[]; search(pattern: string): number; charAt(index: number): string; toLowerCase(): string; toUpperCase(): string; trim(): string; substring(start: number, end?: number): string; charCodeAt(index: number): number; [Symbol.iterator](): IterableIterator; } interface StringConstructor { new (value?: any): String; fromCharCode(...codes: number[]): string; } declare var String: StringConstructor; interface Math { pow(x: number, y: number): number; max(...values: number[]): number; min(...values: number[]): number; sqrt(x: number): number; abs(x: number): number; ceil(x: number): number; floor(x: number): number; } declare var Math: Math; interface TypedPropertyDescriptor {} interface Console { log(...data: any[]): void; } declare var console: Console; interface MapConstructor { new (): Map; set(key: any, value: any): void; get(key: any): any; has(key: any): boolean; size: number; delete(key: any): boolean; clear(): void; forEach( callbackfn: (value: any, key: any, map: Map) => void, ): void; } declare var Map: MapConstructor; interface SetConstructor { new (): Set; add(key: any): void; has(key: any): boolean; delete(key: any): boolean; clear(): void; size: number; forEach(callbackfn: (value: any, key: any, map: Set) => void): void; } declare var Set: SetConstructor; interface PromiseConstructor { new ( executor: ( resolve: (value: T) => void, reject: (reason?: any) => void, ) => void, ): Promise; reject(reason?: any): Promise; resolve(): Promise; resolve(value: T): Promise; } declare var Promise: PromiseConstructor; interface DateConstructor { new (): Date; new (value: number | string): Date; new ( year: number, monthIndex: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number, ): Date; parse(s: string): number; UTC( year: number, monthIndex: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number, ): number; now(): number; } declare var Date: DateConstructor; interface JSON { parse( text: string, reviver?: (this: any, key: string, value: any) => any, ): any; stringify( value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number, ): any; stringify( value: any, replacer?: (number | string)[] | null, space?: string | number, ): any; } /* JSON will fallback to libdyntype */ declare var JSON: any; declare var NaN: number; declare var Infinity: number; interface Error { name: string; message: string; stack?: string; } interface ErrorConstructor { new (message?: string): Error; (message?: string): Error; readonly prototype: Error; } declare var Error: ErrorConstructor; interface Number { toString(radix?: number): string; } interface Object { /** Returns a string representation of an object. */ toString(): string; } interface ObjectConstructor { new (value?: any): Object; (): any; (value: any): any; } declare var Object: ObjectConstructor; interface FunctionConstructor { /** * Creates a new function. * @param args A list of arguments the function accepts. */ new (...args: string[]): Function; (...args: string[]): Function; readonly prototype: Function; } declare var Function: FunctionConstructor; declare function setTimeout( callback: () => void, ms: number, ...args: any[] ): number; declare function clearTimeout(timerid: number): void; interface ArrayBuffer { readonly backing_store: anyref; readonly byteLength: i32; slice(begin?: number, end?: number): ArrayBuffer; } interface ArrayBufferConstructor { new (byteLength: i32): ArrayBuffer; isView(arg: any): arg is ArrayBufferView; } declare var ArrayBuffer: ArrayBufferConstructor; interface DataView { readonly buffer: ArrayBuffer; readonly byteLength: i32; readonly byteOffset: i32; getFloat32(byteOffset: number, littleEndian?: boolean): number; getFloat64(byteOffset: number, littleEndian?: boolean): number; getInt8(byteOffset: number): number; getInt16(byteOffset: number, littleEndian?: boolean): number; getInt32(byteOffset: number, littleEndian?: boolean): number; getUint8(byteOffset: number): number; getUint16(byteOffset: number, littleEndian?: boolean): number; getUint32(byteOffset: number, littleEndian?: boolean): number; setFloat32(byteOffset: number, value: number, littleEndian?: boolean): void; setFloat64(byteOffset: number, value: number, littleEndian?: boolean): void; setInt8(byteOffset: number, value: number): void; setInt16(byteOffset: number, value: number, littleEndian?: boolean): void; setInt32(byteOffset: number, value: number, littleEndian?: boolean): void; setUint8(byteOffset: number, value: number): void; setUint16(byteOffset: number, value: number, littleEndian?: boolean): void; setUint32(byteOffset: number, value: number, littleEndian?: boolean): void; } interface DataViewConstructor { new ( buffer: ArrayBuffer, byteOffset?: number, byteLength?: number, ): DataView; } declare var DataView: DataViewConstructor; ================================================ FILE: lib/builtin/lib_builtin.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ /* Workaround: since console is not used as `declare var console: Console;` in type.d.ts * so we should keep this definition here. */ export declare class Console { log(...values: any[]): void; } export class Math { pow(x: number, y: number): number { let res = 1; let power = y < 0 ? -y : y; while (power > 0) { res = res * x; power--; } res = y < 0 ? 1 / res : res; return res; } max(...x: number[]): number { const arrLen = x.length; let res = x[0]; for (let i = 1; i < arrLen; i++) { if (res < x[i]) { res = x[i]; } } return res; } min(...x: number[]): number { const arrLen = x.length; let res = x[0]; for (let i = 1; i < arrLen; i++) { if (res > x[i]) { res = x[i]; } } return res; } } export class ArrayBufferConstructor { isView(arg: any) { /* workaround: TypedArray is not supported */ if (arg instanceof DataView) { return true; } else { return false; } } } ================================================ FILE: package.json ================================================ { "name": "Wasmnizer-ts", "version": "0.0.1", "description": "", "main": "index.js", "type": "module", "directories": { "doc": "doc", "test": "test" }, "scripts": { "postinstall": "npm run setup-hooks", "compile": "", "pretest": "npm run compile", "test": "mocha", "test:integration": "mocha tests/integration/*.test.ts", "test:samples": "mocha tests/samples/*.test.ts", "test:unit": "mocha 'tests/unit/*.test.ts'", "cover": "c8 npm run test:unit", "lint": "npx lint-staged", "setup-hooks": "node scripts/link_hooks.js", "build": "npx tsc && npm run build:lib", "release": "npx tsc -p tsconfig.release.json && npm run build:lib", "build:lib": "node scripts/copy_lib_files.js build" }, "repository": { "type": "git", "url": "git+https://github.com/intel/Wasmnizer-ts.git" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "log4js": "^6.8.0", "long": "^5.2.1", "ts-node": "^10.9.1", "typescript": "^4.7.4" }, "devDependencies": { "@types/chai": "^4.3.3", "@types/minimist": "^1.2.2", "@types/mocha": "^9.1.1", "@types/node": "^18.6.5", "@types/sinon": "^10.0.13", "@typescript-eslint/eslint-plugin": "^5.33.1", "@typescript-eslint/parser": "^5.33.1", "@typescript-eslint/utils": "^5.33.0", "binaryen": "^116.0.0", "c8": "^7.12.0", "chai": "^4.3.6", "eslint": "^8.22.0", "eslint-config-prettier": "^8.5.0", "eslint-formatter-autolinkable-stylish": "1.2.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-jsdoc": "^39.3.6", "eslint-plugin-no-null": "^1.0.2", "eslint-plugin-prettier": "^4.2.1", "lint-staged": "^13.0.3", "minimist": "^1.2.6", "mocha": "^10.0.0", "prettier": "^2.7.1", "sinon": "^14.0.0", "stacktrace-js": "^2.0.2" }, "lint-staged": { "*.ts": [ "eslint --ignore-path .eslintignore --config .eslintrc.json --fix --quiet '**/*.ts'", "prettier --ignore-path .prettierignore --config .prettierrc.json --write '**/*.ts'" ] }, "bugs": { "url": "https://github.com/intel/Wasmnizer-ts/issues" }, "homepage": "https://github.com/intel/Wasmnizer-ts#readme" } ================================================ FILE: runtime-library/CMakeLists.txt ================================================ # # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # cmake_minimum_required(VERSION 2.8) project(iwasm_gc) if (NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif () if (NOT DEFINED USE_SIMPLE_LIBDYNTYPE) set(USE_SIMPLE_LIBDYNTYPE 0) endif () set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdata-sections -ffunction-sections -Wformat") if (NOT "${WAMR_BUILD_PLATFORM}" STREQUAL "darwin") set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror") set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") endif() if (USE_SANITIZER EQUAL 1) message("Sanitizer enabled") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=signed-integer-overflow \ -fsanitize=undefined -fsanitize=address \ -fno-sanitize=bounds,bounds-strict,alignment \ -fno-sanitize-recover -Wall -Werror -Wformat") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=signed-integer-overflow \ -fsanitize=undefined -fsanitize=address \ -fno-sanitize=bounds,bounds-strict,alignment \ -fno-sanitize-recover -Wall -Werror -Wformat") endif () ## WAMR include(${CMAKE_CURRENT_LIST_DIR}/wamr_config.cmake) add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) ## quickjs if (NOT USE_SIMPLE_LIBDYNTYPE EQUAL 1) set(QUICKJS_SRC_DIR ${CMAKE_CURRENT_LIST_DIR}/deps/quickjs) include_directories(${QUICKJS_SRC_DIR}) set(QUICKJS_SOURCE ${QUICKJS_SRC_DIR}/cutils.c ${QUICKJS_SRC_DIR}/libregexp.c ${QUICKJS_SRC_DIR}/libunicode.c ${QUICKJS_SRC_DIR}/quickjs.c) # Ignore warnings of QuickJS set_source_files_properties( ${QUICKJS_SOURCE} PROPERTIES COMPILE_FLAGS "-w" ) endif () ## libdyntype set(LIBDYNTYPE_DIR ${CMAKE_CURRENT_LIST_DIR}/libdyntype) include (${LIBDYNTYPE_DIR}/libdyntype.cmake) ## stdlib set(STDLIB_DIR ${CMAKE_CURRENT_LIST_DIR}/stdlib) include_directories(${STDLIB_DIR}) set(STDLIB_SOURCE ${STDLIB_DIR}/lib_console.c ${STDLIB_DIR}/lib_array.c ${STDLIB_DIR}/lib_timer.c ) ## struct-indirect set(STRUCT_INDIRECT_DIR ${CMAKE_CURRENT_LIST_DIR}/struct-indirect) include_directories(${STRUCT_INDIRECT_DIR}) set(STRUCT_INDIRECT_SOURCE ${STRUCT_INDIRECT_DIR}/lib_struct_indirect.c ) ## utils set(UTILS_DIR ${CMAKE_CURRENT_LIST_DIR}/utils) include_directories(${UTILS_DIR}) set(TYPE_UTILS_SOURCE ${UTILS_DIR}/type_utils.c ) set(OBJECT_UTILS_SOURCE ${UTILS_DIR}/object_utils.c ) set(WAMR_UTILS_SOURCE ${UTILS_DIR}/wamr_utils.c ) include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) add_executable(iwasm_gc main.c ${UNCOMMON_SHARED_SOURCE} ${QUICKJS_SOURCE} ${LIBDYNTYPE_SRC} ${STDLIB_SOURCE} ${STRUCT_INDIRECT_SOURCE} ${TYPE_UTILS_SOURCE} ${OBJECT_UTILS_SOURCE} ${WAMR_UTILS_SOURCE} ) target_link_libraries (iwasm_gc vmlib -lm -ldl -lpthread) ================================================ FILE: runtime-library/LICENSE ================================================ 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 APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. 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. --- LLVM Exceptions to the Apache 2.0 License ---- As an exception, if, as a result of your compiling your source code, portions of this Software are embedded into an Object form of such source code, you may redistribute such embedded portions in such Object form without complying with the conditions of Sections 4(a), 4(b) and 4(d) of the License. In addition, if you combine or link compiled forms of this Software with software that is licensed under the GPLv2 ("Combined Software") and if a court of competent jurisdiction determines that the patent provision (Section 3), the indemnity provision (Section 9) or other Section of the License conflicts with the conditions of the GPLv2, you may retroactively and prospectively choose to deem waived or otherwise exclude such Section(s) of the License, but only in their entirety and only with respect to the Combined Software. ================================================ FILE: runtime-library/README.md ================================================ # ts2wasm runtime library This folder contains code to be built into WAMR runtime to expose required APIs for executing wasm modules generated by ts2wasm compiler, including: - libdyntype API: APIs for supporting dynamic objects - struct-dyn API: APIs for accessing wasmGC struct fields through dynamically calculated index - stdlib API: APIs for providing standard library functions such as `console.log` ## Build ``` bash # prepare dependencies cd deps ./download.sh cd - # build runtime mkdir build && cd build cmake .. make ``` This will generate an `iwasm_gc` executable which can be used to execute wasm modules generated by ts2wasm compiler. ## Run ``` bash ./iwasm_gc -f [] # e.g. ./iwasm_gc -f consoleLog builtin_console.wasm ``` ## CMake Configurations - **USE_SANITIZER=1** Enable sanitizer. When enabled, all invalid memory access and memory leaks will be reported, disabled by default. - **WAMR_BUILD_TARGET** Build target, default is `X86_64`. - **WAMR_BUILD_FAST_INTERP** Enable fast interpreter, enabled by default. - **WAMR_GC_IN_EVERY_ALLOCATION** Enable GC in every allocation. When enabled, the garbage collector will reclaim the heap on every allocation request, this is for testing the GC behaviour, disabled by default. - **WAMR_GC_HEAP_SIZE** Set default GC Heap size (in bytes), the default value is `131072` (128KB) ================================================ FILE: runtime-library/build.sh ================================================ #!/bin/bash # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception SCRIPT=$(readlink -f "$0") SCRIPTPATH=$(dirname "$SCRIPT") cd ${SCRIPTPATH}/deps ./download.sh cd ${SCRIPTPATH} mkdir -p ${SCRIPTPATH}/build && cd ${SCRIPTPATH}/build cmake .. $* make -j$(nproc) ================================================ FILE: runtime-library/deps/.gitignore ================================================ quickjs/ wamr-gc/ ================================================ FILE: runtime-library/deps/download.sh ================================================ #!/bin/bash # # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # if [ ! -d "quickjs" ]; then git clone --depth=1 https://github.com/wasm-micro-runtime/quickjs.git quickjs fi if [ ! -d "wamr-gc" ]; then git clone --branch main --depth=1 https://github.com/bytecodealliance/wasm-micro-runtime.git wamr-gc fi ================================================ FILE: runtime-library/libdyntype/CMakeLists.txt ================================================ # # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # cmake_minimum_required(VERSION 2.8) project(libdyntype) set(QUICKJS_SRC_DIR ${CMAKE_CURRENT_LIST_DIR}/../deps/quickjs) set(UTILS_DIR ${CMAKE_CURRENT_LIST_DIR}/../utils) set(WAMR_DIR ${CMAKE_CURRENT_LIST_DIR}/../deps/wamr-gc) set(STRUCT_INDIRECT_DIR ${CMAKE_CURRENT_LIST_DIR}/../struct-indirect) include_directories(.) include_directories(${QUICKJS_SRC_DIR}) include_directories(${STRUCT_INDIRECT_DIR}) include_directories(${UTILS_DIR}) include(${CMAKE_CURRENT_LIST_DIR}/../wamr_config.cmake) add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) set(LIB_QUICKJS ${QUICKJS_SRC_DIR}/cutils.c ${QUICKJS_SRC_DIR}/libregexp.c ${QUICKJS_SRC_DIR}/libunicode.c ${QUICKJS_SRC_DIR}/quickjs.c) add_library(quickjs ${LIB_QUICKJS}) set(STRUCT_INDIRECT_SOURCE ${STRUCT_INDIRECT_DIR}/lib_struct_indirect.c ) set(UTILS_SOURCE ${UTILS_DIR}/object_utils.c ${UTILS_DIR}/type_utils.c ${UTILS_DIR}/wamr_utils.c ) include(${CMAKE_CURRENT_LIST_DIR}/libdyntype.cmake) add_library(dyntype ${LIBDYNTYPE_SRC} ${UTILS_SOURCE} ${STRUCT_INDIRECT_SOURCE} ) target_link_libraries(dyntype quickjs vmlib) ================================================ FILE: runtime-library/libdyntype/README.md ================================================ # Dynamic type library Dynamic type library is an independent library which provide APIs to create/operate the dynamic values, it's part of the ts2wasm-runtime-library. ## Unit test ``` bash cd test mkdir build && cd build cmake .. make make test # run the tests ``` ### view coverage report ``` bash # After executing make test make cov-test cd result python3 -m http.server # And then open the browser and navigate to http://localhost:8000 ``` ### debug tests ``` bash # After building gdb ./object_property_test # or any other targets ``` ### enable sanitizer Sanitizer helps to detect invalid memory access, memory leak, undefined behavior and so on. Currently it is disabled by default, to enable it, pass `-DUNITTEST_USE_SANITIZER=1` during cmake configuration ``` bash cmake .. -DUNITTEST_USE_SANITIZER=1 ``` ================================================ FILE: runtime-library/libdyntype/dynamic-qjs/context.c ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "type.h" #include "pure_dynamic.h" static dyn_ctx_t g_dynamic_context = NULL; JSValue * dynamic_dup_value(JSContext *ctx, JSValue value) { JSValue *ptr = js_malloc(ctx, sizeof(value)); if (!ptr) { return NULL; } memcpy(ptr, &value, sizeof(value)); return ptr; } /******************* Initialization and destroy *****************/ dyn_ctx_t dynamic_context_init() { JSClassID class_id; dyn_ctx_t ctx = NULL; if (g_dynamic_context) { return g_dynamic_context; } ctx = malloc(sizeof(DynTypeContext)); if (!ctx) { return NULL; } memset(ctx, 0, sizeof(DynTypeContext)); ctx->js_rt = JS_NewRuntime(); if (!ctx->js_rt) { goto fail; } ctx->js_ctx = JS_NewContext(ctx->js_rt); if (!ctx->js_ctx) { goto fail; } ctx->js_undefined = dynamic_dup_value(ctx->js_ctx, JS_UNDEFINED); if (!ctx->js_undefined) { goto fail; } ctx->js_null = dynamic_dup_value(ctx->js_ctx, JS_NULL); if (!ctx->js_null) { goto fail; } class_id = 0; ctx->extref_class_id = JS_NewClassID(&class_id); (void)class_id; g_dynamic_context = ctx; return ctx; fail: dynamic_context_destroy(ctx); return NULL; } dyn_ctx_t dynamic_context_init_with_opt(dyn_options_t *options) { // TODO return NULL; } void dynamic_context_destroy(dyn_ctx_t ctx) { if (ctx) { if (ctx->js_undefined) { js_free(ctx->js_ctx, ctx->js_undefined); } if (ctx->js_null) { js_free(ctx->js_ctx, ctx->js_null); } if (ctx->js_ctx) { JS_FreeContext(ctx->js_ctx); } if (ctx->js_rt) { JS_FreeRuntime(ctx->js_rt); } free(ctx); } g_dynamic_context = NULL; } dyn_ctx_t dynamic_get_context() { return g_dynamic_context; } ================================================ FILE: runtime-library/libdyntype/dynamic-qjs/fallback.c ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "quickjs-wamr.h" #include "type.h" extern JSValue * dynamic_dup_value(JSContext *ctx, JSValue value); /******************* function fallback *******************/ dyn_value_t dynamic_invoke(dyn_ctx_t ctx, const char *name, dyn_value_t obj, int argc, dyn_value_t *args) { JSValue js_obj = *(JSValue *)obj; uint64_t total_size; JSValue *argv = NULL, v = { 0 }; dyn_value_t res = NULL; total_size = sizeof(JSValue) * argc; if (total_size > 0) { argv = js_malloc(ctx->js_ctx, total_size); if (!argv) { return NULL; } } for (int i = 0; i < argc; i++) { argv[i] = *(JSValue *)args[i]; } if (name && name[0] != '\0') { JSClassCall *call_func = NULL; JSAtom atom = find_atom(ctx->js_ctx, name); JSValue func = JS_GetProperty(ctx->js_ctx, js_obj, atom); JSObject *func_obj; uint32_t class_id; if (!JS_IsFunction(ctx->js_ctx, func)) { JS_FreeAtom(ctx->js_ctx, atom); JS_FreeValue(ctx->js_ctx, func); goto end; } func_obj = JS_VALUE_GET_OBJ(func); class_id = getClassIdFromObject(func_obj); call_func = getCallByClassId(ctx->js_rt, class_id); if (!call_func) { JS_FreeAtom(ctx->js_ctx, atom); JS_FreeValue(ctx->js_ctx, func); goto end; } // flags is 0 because quickjs.c:17047 v = call_func(ctx->js_ctx, func, js_obj, argc, argv, 0); JS_FreeAtom(ctx->js_ctx, atom); JS_FreeValue(ctx->js_ctx, func); } else { if (!JS_IsFunction(ctx->js_ctx, js_obj)) { goto end; } v = JS_Call(ctx->js_ctx, js_obj, JS_UNDEFINED, argc, argv); } res = dynamic_dup_value(ctx->js_ctx, v); end: if (argv) { js_free(ctx->js_ctx, argv); } return res; } int dynamic_execute_pending_jobs(dyn_ctx_t ctx) { JSContext *js_ctx1; return JS_ExecutePendingJob(JS_GetRuntime(ctx->js_ctx), &js_ctx1); } ================================================ FILE: runtime-library/libdyntype/dynamic-qjs/object.c ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "quickjs-wamr.h" #include "libdyntype_export.h" #include "type.h" extern JSValue * dynamic_dup_value(JSContext *ctx, JSValue value); /******************* builtin type compare *******************/ static inline bool number_cmp(double lhs, double rhs, cmp_operator operator_kind) { bool res = false; switch (operator_kind) { case LessThanToken: { res = lhs < rhs; break; } case GreaterThanToken: { res = lhs > rhs; break; } case EqualsEqualsToken: case EqualsEqualsEqualsToken: { res = lhs == rhs; break; } case LessThanEqualsToken: { res = lhs <= rhs; break; } case GreaterThanEqualsToken: { res = lhs >= rhs; break; } case ExclamationEqualsToken: case ExclamationEqualsEqualsToken: { res = lhs != rhs; break; } } return res; } static inline bool string_cmp(const char *lhs, const char *rhs, cmp_operator operator_kind) { bool res = false; int cmp_res = strcmp(lhs, rhs); switch (operator_kind) { case LessThanToken: { res = cmp_res < 0; break; } case GreaterThanToken: { res = cmp_res > 0; break; } case EqualsEqualsToken: case EqualsEqualsEqualsToken: { res = cmp_res == 0; break; } case LessThanEqualsToken: { res = cmp_res <= 0; break; } case GreaterThanEqualsToken: { res = cmp_res >= 0; break; } case ExclamationEqualsToken: case ExclamationEqualsEqualsToken: { res = cmp_res != 0; break; } } return res; } static inline bool bool_cmp(bool lhs, bool rhs, cmp_operator operator_kind) { bool res = false; switch (operator_kind) { case LessThanToken: { res = lhs < rhs; break; } case GreaterThanToken: { res = lhs > rhs; break; } case EqualsEqualsToken: case EqualsEqualsEqualsToken: { res = lhs == rhs; break; } case LessThanEqualsToken: { res = lhs <= rhs; break; } case GreaterThanEqualsToken: { res = lhs >= rhs; break; } case ExclamationEqualsToken: case ExclamationEqualsEqualsToken: { res = lhs != rhs; break; } } return res; } static inline bool cmp_operator_has_equal_token(cmp_operator operator_kind) { if (operator_kind == EqualsEqualsToken || operator_kind == EqualsEqualsEqualsToken || operator_kind == LessThanEqualsToken || operator_kind == GreaterThanEqualsToken) { return true; } return false; } /******************* exterf obj boxing *******************/ static dyn_type_t quickjs_type_to_dyn_type(int quickjs_tag) { switch (quickjs_tag) { #define XX(qtag, dyntype) \ case qtag: \ return dyntype; XX(0, DynNull); XX(69, DynUndefined); XX(73, DynObject); XX(71, DynBoolean); XX(70, DynNumber); XX(72, DynString); // XX(27, DynFunction); // TODO XX(74, DynSymbol); // XX(139, DynBigInt); // TODO #undef XX default: return DynUnknown; } return DynUnknown; } static JSValue WasmCallBackDataForJS(JSContext *ctx, JSValueConst this_obj, int argc, JSValueConst *argv, int magic, JSValue *func_data) { JSValue ret; void *vfunc = JS_GetOpaque(func_data[0], JS_CLASS_OBJECT); void *exec_env = JS_GetOpaque(func_data[1], JS_CLASS_OBJECT); dyn_ctx_t dyntype_ctx = JS_GetOpaque(func_data[2], JS_CLASS_OBJECT); dyn_value_t *args = NULL; dyn_value_t this_dyn_obj = NULL; uint64_t total_size; dyntype_callback_dispatcher_t cb_dispatcher = NULL; total_size = sizeof(dyn_value_t) * argc; args = malloc(total_size); if (!args) { return JS_NULL; } for (int i = 0; i < argc; i++) { args[i] = dynamic_dup_value(ctx, *(argv + i)); } this_dyn_obj = dynamic_dup_value(ctx, this_obj); cb_dispatcher = dyntype_get_callback_dispatcher(); if (cb_dispatcher) { dyn_value_t res_boxed = cb_dispatcher( exec_env, dyntype_ctx, vfunc, this_dyn_obj, argc, args); ret = *(JSValue *)(res_boxed); if (res_boxed != dyntype_ctx->js_undefined && res_boxed != dyntype_ctx->js_null) { js_free(dyntype_ctx->js_ctx, res_boxed); } } else { ret = JS_ThrowInternalError( ctx, "external callback dispatcher not registered"); } if (args) { for (int i = 0; i < argc; i++) { js_free(ctx, args[i]); } free(args); } if (this_dyn_obj) { js_free(ctx, this_dyn_obj); } return ret; } static JSValue new_function_wrapper(dyn_ctx_t ctx, void *vfunc, void *opaque) { JSValue data_hold[3]; data_hold[0] = JS_NewObject(ctx->js_ctx); JS_SetOpaque(data_hold[0], vfunc); data_hold[1] = JS_NewObject(ctx->js_ctx); JS_SetOpaque(data_hold[1], opaque); data_hold[2] = JS_NewObject(ctx->js_ctx); JS_SetOpaque(data_hold[2], ctx); JSValue func = JS_NewCFunctionData(ctx->js_ctx, WasmCallBackDataForJS, 0, 0, 3, data_hold); // data will be dup inside qjs JS_FreeValue(ctx->js_ctx, data_hold[0]); JS_FreeValue(ctx->js_ctx, data_hold[1]); JS_FreeValue(ctx->js_ctx, data_hold[2]); return func; } /******************* Field access *******************/ dyn_value_t dynamic_new_number(dyn_ctx_t ctx, double value) { JSValue v = JS_NewFloat64(ctx->js_ctx, value); return dynamic_dup_value(ctx->js_ctx, v); } dyn_value_t dynamic_new_boolean(dyn_ctx_t ctx, bool value) { JSValue v = JS_NewBool(ctx->js_ctx, value); return dynamic_dup_value(ctx->js_ctx, v); } #if WASM_ENABLE_STRINGREF != 0 dyn_value_t dynamic_new_string(dyn_ctx_t ctx, const void *stringref) { JSValue js_str = JS_MKPTR(JS_TAG_STRING, (void *)stringref); return dynamic_dup_value(ctx->js_ctx, JS_DupValue(ctx->js_ctx, js_str)); } #else dyn_value_t dynamic_new_string(dyn_ctx_t ctx, const char *str, int len) { JSValue v = JS_NewStringLen(ctx->js_ctx, str, len); if (JS_IsException(v)) { return NULL; } return dynamic_dup_value(ctx->js_ctx, v); } #endif dyn_value_t dynamic_new_undefined(dyn_ctx_t ctx) { return ctx->js_undefined; } dyn_value_t dynamic_new_null(dyn_ctx_t ctx) { return ctx->js_null; } dyn_value_t dynamic_new_object(dyn_ctx_t ctx) { JSValue v = JS_NewObject(ctx->js_ctx); if (JS_IsException(v)) { return NULL; } return dynamic_dup_value(ctx->js_ctx, v); } dyn_value_t dynamic_parse_json(dyn_ctx_t ctx, const char *str) { JSValue v = JS_ParseJSON(ctx->js_ctx, str, strlen(str), NULL); if (JS_IsException(v)) { return NULL; } return dynamic_dup_value(ctx->js_ctx, v); } dyn_value_t dynamic_new_array(dyn_ctx_t ctx, int len) { JSValue v = JS_NewArray(ctx->js_ctx); if (JS_IsException(v)) { return NULL; } if (len) { JSValue vlen = JS_NewInt32(ctx->js_ctx, len); set_array_length1(ctx->js_ctx, JS_VALUE_GET_OBJ(v), vlen, 0); } return dynamic_dup_value(ctx->js_ctx, v); } dyn_value_t dynamic_get_global(dyn_ctx_t ctx, const char *name) { JSAtom atom = find_atom(ctx->js_ctx, name); JSValue global_var = JS_GetGlobalVar(ctx->js_ctx, atom, true); if (JS_IsException(global_var)) { return NULL; } JS_FreeAtom(ctx->js_ctx, atom); return dynamic_dup_value(ctx->js_ctx, global_var); } dyn_value_t dynamic_new_object_with_class(dyn_ctx_t ctx, const char *name, int argc, dyn_value_t *args) { JSValue obj; JSAtom atom = find_atom(ctx->js_ctx, name); JSValue global_var = JS_GetGlobalVar(ctx->js_ctx, atom, true); JSValue *argv = NULL; dyn_value_t res = NULL; uint64_t total_size; if (JS_IsException(global_var)) { goto end; } total_size = sizeof(JSValue) * argc; if (total_size > 0) { argv = js_malloc(ctx->js_ctx, total_size); if (!argv) { goto end; } } for (int i = 0; i < argc; i++) { argv[i] = *(JSValue *)args[i]; } obj = JS_CallConstructorInternal(ctx->js_ctx, global_var, global_var, argc, argv, 0); res = dynamic_dup_value(ctx->js_ctx, obj); end: JS_FreeAtom(ctx->js_ctx, atom); JS_FreeValue(ctx->js_ctx, global_var); if (argv) { js_free(ctx->js_ctx, argv); } return res; } dyn_value_t dynamic_new_extref(dyn_ctx_t ctx, void *ptr, external_ref_tag tag, void *opaque) { JSValue tag_v, ref_v, v; if (tag != ExtObj && tag != ExtFunc && tag != ExtArray) { return NULL; } if (tag == ExtFunc) { v = new_function_wrapper(ctx, ptr, opaque); } else { v = JS_NewObject(ctx->js_ctx); } if (JS_IsException(v)) { return NULL; } tag_v = JS_NewInt32(ctx->js_ctx, (int)tag); ref_v = JS_NewInt32(ctx->js_ctx, (int32_t)(uintptr_t)ptr); JS_DefinePropertyValueStr(ctx->js_ctx, v, "@tag", tag_v, 0); JS_DefinePropertyValueStr(ctx->js_ctx, v, "@ref", ref_v, 0); return dynamic_dup_value(ctx->js_ctx, v); } int dynamic_set_elem(dyn_ctx_t ctx, dyn_value_t obj, int index, dyn_value_t elem) { JSValue *obj_ptr = (JSValue *)obj; JSValue *elem_ptr = (JSValue *)elem; if (!JS_IsArray(ctx->js_ctx, *obj_ptr)) { return -DYNTYPE_TYPEERR; } if (index < 0) { return -DYNTYPE_TYPEERR; } if (JS_SetPropertyUint32(ctx->js_ctx, *obj_ptr, index, JS_DupValue(ctx->js_ctx, *elem_ptr)) < 0) { return -DYNTYPE_EXCEPTION; } return DYNTYPE_SUCCESS; } dyn_value_t dynamic_get_elem(dyn_ctx_t ctx, dyn_value_t obj, int index) { JSValue val; JSValue *obj_ptr = (JSValue *)obj; if (!JS_IsArray(ctx->js_ctx, *obj_ptr)) { return NULL; } if (index < 0) return dynamic_new_undefined(ctx); val = JS_GetPropertyUint32(ctx->js_ctx, *obj_ptr, index); if (JS_IsException(val)) { return NULL; } return dynamic_dup_value(ctx->js_ctx, val); } int dynamic_set_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop, dyn_value_t value) { int ret; JSValue *val; JSValue *obj_ptr = (JSValue *)obj; if (!JS_IsObject(*obj_ptr)) { return -DYNTYPE_TYPEERR; } val = (JSValue *)value; ret = JS_SetPropertyStr(ctx->js_ctx, *obj_ptr, prop, JS_DupValue(ctx->js_ctx, *val)) ? DYNTYPE_SUCCESS : -DYNTYPE_EXCEPTION; return ret; } int dynamic_define_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop, dyn_value_t desc) { int res; JSValue *obj_ptr = (JSValue *)obj; JSValue *desc_ptr = (JSValue *)desc; JSAtom atom; if (!JS_IsObject(*obj_ptr)) { return -DYNTYPE_TYPEERR; } if (!JS_IsObject(*desc_ptr)) { return -DYNTYPE_TYPEERR; } atom = JS_NewAtom(ctx->js_ctx, prop); if (atom == JS_ATOM_NULL) { return -DYNTYPE_EXCEPTION; } // It will only return TRUE or EXCEPTION, because of JS_PROP_THROW flag res = JS_DefinePropertyDesc1(ctx->js_ctx, *obj_ptr, atom, *desc_ptr, JS_PROP_THROW); JS_FreeAtom(ctx->js_ctx, atom); return res == -1 ? -DYNTYPE_EXCEPTION : DYNTYPE_SUCCESS; } dyn_value_t dynamic_get_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop) { JSValue *obj_ptr = (JSValue *)obj; JSValue *ptr = NULL; JSValue val; if (!JS_IsObject(*obj_ptr) && !JS_IsString(*obj_ptr)) { return ctx->js_undefined; } val = JS_GetPropertyStr(ctx->js_ctx, *obj_ptr, prop); if (JS_IsException(val)) { return NULL; } ptr = dynamic_dup_value(ctx->js_ctx, val); return ptr; } int dynamic_has_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop) { int res; JSAtom atom; JSValue *obj_ptr = (JSValue *)obj; if (!JS_IsObject(*obj_ptr)) { return -DYNTYPE_TYPEERR; } atom = JS_NewAtom(ctx->js_ctx, prop); if (atom == JS_ATOM_NULL) { return -DYNTYPE_EXCEPTION; } res = JS_HasProperty(ctx->js_ctx, *obj_ptr, atom); JS_FreeAtom(ctx->js_ctx, atom); if (res == -1) { return -DYNTYPE_EXCEPTION; } return res == 0 ? DYNTYPE_FALSE : DYNTYPE_TRUE; } int dynamic_delete_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop) { JSValue *obj_ptr = (JSValue *)obj; JSAtom atom; if (dynamic_has_property(ctx, obj, prop) != DYNTYPE_TRUE) { return -DYNTYPE_FALSE; } atom = JS_NewAtom(ctx->js_ctx, prop); if (atom == JS_ATOM_NULL) { return -DYNTYPE_EXCEPTION; } int res = JS_DeleteProperty(ctx->js_ctx, *obj_ptr, atom, 0); JS_FreeAtom(ctx->js_ctx, atom); if (res == -1) { return -DYNTYPE_EXCEPTION; } return res == 0 ? DYNTYPE_FALSE : DYNTYPE_TRUE; } dyn_value_t dynamic_get_keys(dyn_ctx_t ctx, dyn_value_t obj) { dyn_value_t object_obj, res = NULL; object_obj = dyntype_get_global(ctx, "Object"); res = dyntype_invoke(ctx, "keys", object_obj, 1, &obj); dyntype_release(ctx, object_obj); return res; } /******************* Runtime type checking *******************/ bool dynamic_is_undefined(dyn_ctx_t ctx, dyn_value_t obj) { JSValue *ptr = (JSValue *)obj; return (bool)JS_IsUndefined(*ptr); } bool dynamic_is_null(dyn_ctx_t ctx, dyn_value_t obj) { JSValue *ptr = (JSValue *)obj; return (bool)JS_IsNull(*ptr); } bool dynamic_is_bool(dyn_ctx_t ctx, dyn_value_t obj) { JSValue *ptr = (JSValue *)obj; return (bool)JS_IsBool(*ptr); } int dynamic_to_bool(dyn_ctx_t ctx, dyn_value_t bool_obj, bool *pres) { JSValue *ptr = (JSValue *)bool_obj; if (!JS_IsBool(*ptr)) { return -DYNTYPE_TYPEERR; } *pres = (bool)JS_ToBool(ctx->js_ctx, *ptr); return DYNTYPE_SUCCESS; } bool dynamic_is_number(dyn_ctx_t ctx, dyn_value_t obj) { JSValue *ptr = (JSValue *)obj; return (bool)JS_IsNumber(*ptr); } int dynamic_to_number(dyn_ctx_t ctx, dyn_value_t obj, double *pres) { JSValue *ptr = (JSValue *)obj; if (!JS_IsNumber(*ptr)) { return -DYNTYPE_TYPEERR; } *pres = (JS_VALUE_GET_TAG(*ptr) == JS_TAG_INT ? JS_VALUE_GET_INT(*ptr) : JS_VALUE_GET_FLOAT64(*ptr)); return DYNTYPE_SUCCESS; } bool dynamic_is_string(dyn_ctx_t ctx, dyn_value_t obj) { JSValue *ptr = (JSValue *)obj; return JS_IsString(*ptr); } #if WASM_ENABLE_STRINGREF != 0 void * dynamic_to_string(dyn_ctx_t ctx, dyn_value_t obj) { JSValue js_str = *(JSValue *)obj; JS_DupValue(ctx->js_ctx, js_str); return JS_VALUE_GET_PTR(js_str); } #endif int dynamic_to_cstring(dyn_ctx_t ctx, dyn_value_t str_obj, char **pres) { JSValue *ptr = (JSValue *)str_obj; *pres = (char *)JS_ToCString(ctx->js_ctx, *ptr); if (*pres == NULL) { return -DYNTYPE_EXCEPTION; } return DYNTYPE_SUCCESS; } void dynamic_free_cstring(dyn_ctx_t ctx, char *str) { JS_FreeCString(ctx->js_ctx, (const char *)str); } bool dynamic_is_object(dyn_ctx_t ctx, dyn_value_t obj) { JSValue *ptr = (JSValue *)obj; return (bool)JS_IsObject(*ptr); } bool dynamic_is_function(dyn_ctx_t ctx, dyn_value_t obj) { JSValue *ptr = (JSValue *)obj; return (bool)JS_IsFunction(ctx->js_ctx, *ptr); } bool dynamic_is_array(dyn_ctx_t ctx, dyn_value_t obj) { JSValue *ptr = (JSValue *)obj; return (bool)JS_IsArray(ctx->js_ctx, *ptr); } bool dynamic_is_extref(dyn_ctx_t ctx, dyn_value_t obj) { if (!dynamic_is_object(ctx, obj)) { return false; } return dynamic_has_property(ctx, obj, "@tag") == DYNTYPE_TRUE ? true : false; } int dynamic_to_extref(dyn_ctx_t ctx, dyn_value_t obj, void **pres) { JSValue *ref_v; JSValue *tag_v; int tag; if (dynamic_is_extref(ctx, obj) == DYNTYPE_FALSE) { return -DYNTYPE_TYPEERR; } tag_v = dynamic_get_property(ctx, obj, "@tag"); ref_v = dynamic_get_property(ctx, obj, "@ref"); *pres = (void *)(uintptr_t)JS_VALUE_GET_INT(*ref_v); tag = JS_VALUE_GET_INT(*tag_v); js_free(ctx->js_ctx, tag_v); js_free(ctx->js_ctx, ref_v); return tag; } bool dynamic_is_exception(dyn_ctx_t ctx, dyn_value_t value) { JSValue *ptr = (JSValue *)value; return (bool)JS_IsException(*ptr); } bool dynamic_is_falsy(dyn_ctx_t ctx, dyn_value_t value) { bool res; if (dynamic_is_extref(ctx, value)) { res = false; } else if (dynamic_is_object(ctx, value)) { res = false; } else if (dynamic_is_undefined(ctx, value) || dynamic_is_null(ctx, value)) { res = true; } else if (dynamic_is_bool(ctx, value)) { bool b; dynamic_to_bool(ctx, value, &b); res = !b; } else if (dynamic_is_number(ctx, value)) { double num; dynamic_to_number(ctx, value, &num); res = num == 0; } else if (dynamic_is_string(ctx, value)) { char *str; dynamic_to_cstring(ctx, value, &str); res = strcmp(str, "") == 0; dynamic_free_cstring(ctx, str); } else { res = false; } return res; } /******************* Type equivalence *******************/ dyn_type_t dynamic_typeof(dyn_ctx_t ctx, dyn_value_t obj) { JSValueConst *ptr = (JSValueConst *)obj; if (dynamic_is_extref(ctx, obj)) { int tag; void *ref; tag = dynamic_to_extref(ctx, obj, &ref); if (tag == ExtObj) { return DynExtRefObj; } else if (tag == ExtFunc) { return DynExtRefFunc; } else if (tag == ExtArray) { return DynExtRefArray; } } int q_atom_tag = js_operator_typeof1(ctx->js_ctx, *ptr); dyn_type_t tag = quickjs_type_to_dyn_type(q_atom_tag); return tag; } bool dynamic_type_eq(dyn_ctx_t ctx, dyn_value_t lhs, dyn_value_t rhs) { return dynamic_typeof(ctx, lhs) == dynamic_typeof(ctx, rhs); } bool dynamic_cmp(dyn_ctx_t ctx, dyn_value_t lhs, dyn_value_t rhs, cmp_operator operator_kind) { bool res; dyn_type_t type; if (lhs == rhs) { if (cmp_operator_has_equal_token(operator_kind)) { return true; } else { return false; } } type = dynamic_typeof(ctx, lhs); switch (type) { case DynBoolean: { bool lhs_b = 0, rhs_b = 0; dynamic_to_bool(ctx, lhs, &lhs_b); dynamic_to_bool(ctx, rhs, &rhs_b); res = bool_cmp(lhs_b, rhs_b, operator_kind); break; } case DynNumber: { double lhs_n = 0, rhs_n = 0; dynamic_to_number(ctx, lhs, &lhs_n); dynamic_to_number(ctx, rhs, &rhs_n); res = number_cmp(lhs_n, rhs_n, operator_kind); break; } case DynNull: { if (cmp_operator_has_equal_token(operator_kind)) { res = true; } else { res = false; } break; } case DynUndefined: { /** undefined <= undefined => false*/ if (operator_kind == EqualsEqualsToken || operator_kind == EqualsEqualsEqualsToken) { res = true; } else { res = false; } break; } case DynString: { char *lhs_s, *rhs_s; dynamic_to_cstring(ctx, lhs, &lhs_s); dynamic_to_cstring(ctx, rhs, &rhs_s); res = string_cmp(lhs_s, rhs_s, operator_kind); dynamic_free_cstring(ctx, lhs_s); dynamic_free_cstring(ctx, rhs_s); break; } case DynObject: { /** only allows == / === / != / !== */ if (operator_kind < EqualsEqualsToken) { printf("[runtime library error]: non-equal compare token on " "two any type objects"); } JSValue *lhs_v = (JSValue *)lhs; JSValue *rhs_v = (JSValue *)rhs; res = JS_VALUE_GET_PTR(*lhs_v) == JS_VALUE_GET_PTR(*rhs_v); if (operator_kind == ExclamationEqualsToken || operator_kind == ExclamationEqualsEqualsToken) { res = !res; } break; } default: { res = false; } } return res; } /******************* Subtyping *******************/ dyn_value_t dynamic_new_object_with_proto(dyn_ctx_t ctx, const dyn_value_t proto_obj) { JSValueConst *proto = (JSValueConst *)proto_obj; if (!JS_IsObject(*proto) && !JS_IsNull(*proto)) { return NULL; } JSValue new_obj = JS_NewObjectProto(ctx->js_ctx, *proto); if (JS_IsException(new_obj)) { return NULL; } return dynamic_dup_value(ctx->js_ctx, new_obj); } int dynamic_set_prototype(dyn_ctx_t ctx, dyn_value_t obj, const dyn_value_t proto_obj) { JSValue *obj_ptr = (JSValue *)obj; if (JS_VALUE_GET_TAG(*obj_ptr) == JS_TAG_NULL || JS_VALUE_GET_TAG(*obj_ptr) == JS_TAG_UNDEFINED) { return -DYNTYPE_TYPEERR; } JSValue *proto_obj_ptr = (JSValue *)proto_obj; if (JS_VALUE_GET_TAG(*proto_obj_ptr) != JS_TAG_NULL && JS_VALUE_GET_TAG(*proto_obj_ptr) != JS_TAG_OBJECT) { return -DYNTYPE_TYPEERR; } int res = JS_SetPrototype(ctx->js_ctx, *obj_ptr, *proto_obj_ptr); return res == 1 ? DYNTYPE_SUCCESS : -DYNTYPE_EXCEPTION; } dyn_value_t dynamic_get_prototype(dyn_ctx_t ctx, dyn_value_t obj) { JSValue *obj_ptr = (JSValue *)obj; if (JS_VALUE_GET_TAG(*obj_ptr) == JS_TAG_NULL || JS_VALUE_GET_TAG(*obj_ptr) == JS_TAG_UNDEFINED) { return NULL; } JSValue proto = JS_GetPrototype(ctx->js_ctx, *obj_ptr); if (JS_IsException(proto)) { return NULL; } JSValue *proto1 = dynamic_dup_value(ctx->js_ctx, proto); return proto1; } dyn_value_t dynamic_get_own_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop) { JSValue *obj_ptr = (JSValue *)obj; if (JS_VALUE_GET_TAG(*obj_ptr) != JS_TAG_OBJECT) { return NULL; } JSAtom atom = JS_NewAtom(ctx->js_ctx, prop); if (atom == JS_ATOM_NULL) { return NULL; } JSPropertyDescriptor desc; int res = JS_GetOwnProperty(ctx->js_ctx, &desc, *obj_ptr, atom); JS_FreeAtom(ctx->js_ctx, atom); if (res != 1) { return NULL; } JSValue *v = dynamic_dup_value(ctx->js_ctx, desc.value); return v; } bool dynamic_instanceof(dyn_ctx_t ctx, const dyn_value_t src_obj, const dyn_value_t dst_obj) { JSValue *src = (JSValue *)src_obj; JSValue *dst = (JSValue *)dst_obj; int ret = JS_OrdinaryIsInstanceOf1(ctx->js_ctx, *src, *dst); if (ret == -1) { return -DYNTYPE_EXCEPTION; } return ret == 1 ? DYNTYPE_TRUE : DYNTYPE_FALSE; } /******************* Dumping *******************/ void dynamic_dump_value(dyn_ctx_t ctx, dyn_value_t obj) { JSValue *v = (JSValue *)obj; const char *str; size_t len; str = JS_ToCStringLen(ctx->js_ctx, &len, *v); if (str) { if (JS_IsArray(ctx->js_ctx, *v)) { fwrite("[", 1, 1, stdout); } fwrite(str, 1, len, stdout); if (JS_IsArray(ctx->js_ctx, *v)) { fwrite("]", 1, 1, stdout); } } JS_FreeCString(ctx->js_ctx, str); } int dynamic_dump_value_buffer(dyn_ctx_t ctx, dyn_value_t obj, void *buffer, int len) { JSValue *v = (JSValue *)obj; int res = JS_DumpWithBuffer(ctx->js_rt, v, buffer, len); return res == -1 ? -DYNTYPE_EXCEPTION : res; } static dyn_value_t dynamic_get_exception(dyn_ctx_t ctx) { JSValue val = JS_GetException(ctx->js_ctx); return dynamic_dup_value(ctx->js_ctx, val); } void dynamic_dump_error(dyn_ctx_t ctx) { dyn_value_t error; JSValue val; BOOL is_error; error = dynamic_get_exception(ctx); is_error = JS_IsError(ctx->js_ctx, *(JSValue *)error); dynamic_dump_value(ctx, error); if (is_error) { val = JS_GetPropertyStr(ctx->js_ctx, *(JSValue *)error, "stack"); if (!JS_IsUndefined(val)) { dynamic_dump_value(ctx, dynamic_dup_value(ctx->js_ctx, val)); } JS_FreeValue(ctx->js_ctx, val); } } /******************* Garbage collection *******************/ dyn_value_t dynamic_hold(dyn_ctx_t ctx, dyn_value_t obj) { JSValue *ptr = (JSValue *)obj; if (JS_VALUE_HAS_REF_COUNT(*ptr)) { JS_DupValue(ctx->js_ctx, *ptr); } return dynamic_dup_value(ctx->js_ctx, *ptr); } void dynamic_release(dyn_ctx_t ctx, dyn_value_t obj) { if (obj == NULL) { return; } JSValue *ptr = (JSValue *)(obj); JS_FreeValue(ctx->js_ctx, *ptr); if (obj != ctx->js_undefined && obj != ctx->js_null) { js_free(ctx->js_ctx, obj); } } void dynamic_collect(dyn_ctx_t ctx) { // TODO } /******************* Exception *******************/ dyn_value_t dynamic_throw_exception(dyn_ctx_t ctx, dyn_value_t obj) { JSValue exception_obj; JSValue js_exception; exception_obj = *(JSValue *)obj; js_exception = JS_Throw(ctx->js_ctx, exception_obj); return dynamic_dup_value(ctx->js_ctx, js_exception); } /******************* Special Property Access *******************/ int dynamic_get_array_length(dyn_ctx_t ctx, dyn_value_t obj) { dyn_value_t length_value = NULL; int length = 0; length_value = dynamic_get_property(ctx, obj, "length"); if (!JS_IsNumber(*(JSValue *)length_value)) { return -DYNTYPE_TYPEERR; } length = JS_VALUE_GET_TAG(*(JSValue *)length_value) == JS_TAG_INT ? JS_VALUE_GET_INT(*(JSValue *)length_value) : -DYNTYPE_TYPEERR; if (length_value) { js_free(ctx->js_ctx, length_value); } return length; } ================================================ FILE: runtime-library/libdyntype/dynamic-qjs/pure_dynamic.h ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #ifndef __PURE_DYNAMIC_H_ #define __PURE_DYNAMIC_H_ #include "libdyntype.h" #ifdef __cplusplus extern "C" { #endif /********************************************/ /* APIs exposed to runtime embedder */ /********************************************/ dyn_ctx_t dynamic_context_init(); dyn_ctx_t dynamic_context_init_with_opt(dyn_options_t *options); void dynamic_context_destroy(dyn_ctx_t ctx); int dynamic_execute_pending_jobs(dyn_ctx_t ctx); void dynamic_dump_error(dyn_ctx_t ctx); dyn_value_t dynamic_throw_exception(dyn_ctx_t ctx, dyn_value_t obj); void dynamic_dump_value(dyn_ctx_t ctx, dyn_value_t obj); int dynamic_dump_value_buffer(dyn_ctx_t ctx, dyn_value_t obj, void *buffer, int len); dyn_value_t dynamic_hold(dyn_ctx_t ctx, dyn_value_t obj); void dynamic_release(dyn_ctx_t ctx, dyn_value_t obj); void dynamic_collect(dyn_ctx_t ctx); /********************************************/ /* APIs exposed to wasm application */ /********************************************/ dyn_ctx_t dynamic_get_context(); dyn_value_t dynamic_new_number(dyn_ctx_t ctx, double value); dyn_value_t dynamic_new_boolean(dyn_ctx_t ctx, bool value); #if WASM_ENABLE_STRINGREF != 0 dyn_value_t dynamic_new_string(dyn_ctx_t ctx, const void *stringref); #else dyn_value_t dynamic_new_string(dyn_ctx_t ctx, const char *str, int len); #endif dyn_value_t dynamic_new_undefined(dyn_ctx_t ctx); dyn_value_t dynamic_new_null(dyn_ctx_t ctx); dyn_value_t dynamic_new_object(dyn_ctx_t ctx); dyn_value_t dynamic_new_object_with_proto(dyn_ctx_t ctx, const dyn_value_t proto_obj); dyn_value_t dynamic_new_object_with_class(dyn_ctx_t ctx, const char *name, int argc, dyn_value_t *args); dyn_value_t dynamic_new_array(dyn_ctx_t ctx, int len); dyn_value_t dynamic_new_extref(dyn_ctx_t ctx, void *ptr, external_ref_tag tag, void* opaque); int dynamic_set_elem(dyn_ctx_t ctx, dyn_value_t obj, int index, dyn_value_t elem); dyn_value_t dynamic_get_elem(dyn_ctx_t ctx, dyn_value_t obj, int index); int dynamic_set_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop, dyn_value_t value); dyn_value_t dynamic_get_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop); dyn_value_t dynamic_get_own_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop); int dynamic_define_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop, dyn_value_t desc); int dynamic_has_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop); int dynamic_delete_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop); bool dynamic_is_number(dyn_ctx_t ctx, dyn_value_t obj); int dynamic_to_number(dyn_ctx_t ctx, dyn_value_t obj, double *pres); bool dynamic_is_bool(dyn_ctx_t ctx, dyn_value_t obj); int dynamic_to_bool(dyn_ctx_t ctx, dyn_value_t bool_obj, bool *pres); bool dynamic_is_string(dyn_ctx_t ctx, dyn_value_t obj); #if WASM_ENABLE_STRINGREF != 0 void * dynamic_to_string(dyn_ctx_t ctx, dyn_value_t obj); #endif int dynamic_to_cstring(dyn_ctx_t ctx, dyn_value_t str_obj, char **pres); void dynamic_free_cstring(dyn_ctx_t ctx, char *str); bool dynamic_is_undefined(dyn_ctx_t ctx, dyn_value_t obj); bool dynamic_is_null(dyn_ctx_t ctx, dyn_value_t obj); bool dynamic_is_object(dyn_ctx_t ctx, dyn_value_t obj); bool dynamic_is_function(dyn_ctx_t ctx, dyn_value_t obj); bool dynamic_is_array(dyn_ctx_t ctx, dyn_value_t obj); bool dynamic_is_extref(dyn_ctx_t ctx, dyn_value_t obj); int dynamic_to_extref(dyn_ctx_t ctx, dyn_value_t obj, void **pres); bool dynamic_is_exception(dyn_ctx_t ctx, dyn_value_t value); bool dynamic_is_falsy(dyn_ctx_t ctx, dyn_value_t value); dyn_type_t dynamic_typeof(dyn_ctx_t ctx, dyn_value_t obj); bool dynamic_type_eq(dyn_ctx_t ctx, dyn_value_t lhs, dyn_value_t rhs); bool dynamic_cmp(dyn_ctx_t ctx, dyn_value_t lhs, dyn_value_t rhs, cmp_operator operator_kind); int dynamic_set_prototype(dyn_ctx_t ctx, dyn_value_t obj, const dyn_value_t proto_obj); dyn_value_t dynamic_get_prototype(dyn_ctx_t ctx, dyn_value_t obj); bool dynamic_instanceof(dyn_ctx_t ctx, const dyn_value_t src_obj, const dyn_value_t dst_obj); dyn_value_t dynamic_invoke(dyn_ctx_t ctx, const char *name, dyn_value_t this_obj, int argc, dyn_value_t *args); dyn_value_t dynamic_get_global(dyn_ctx_t ctx, const char *name); dyn_value_t dynamic_get_keys(dyn_ctx_t ctx, dyn_value_t obj); /******************* Special Property Access *******************/ int dynamic_get_array_length(dyn_ctx_t ctx, dyn_value_t obj); #ifdef __cplusplus } #endif #endif /* end of __PURE_DYNAMIC_H_ */ ================================================ FILE: runtime-library/libdyntype/dynamic-qjs/type.h ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "libdyntype.h" #include "cutils.h" #include "quickjs.h" #include typedef struct DynTypeContext { JSRuntime *js_rt; JSContext *js_ctx; JSValue *js_undefined; JSValue *js_null; JSClassID extref_class_id; JSValue *extref_class; } DynTypeContext; ================================================ FILE: runtime-library/libdyntype/dynamic-simple/README.md ================================================ # libdyntype with simple implementation This folder provides a simplified implementation of libdyntype without dependency to QuickJS, this may be useful especially on some resource constraint devices. > Note: this simple implementation of libdyntype doesn't support these features: > - Use JavaScript builtin object and their methods (such as JSON, JSON.stringify, Map, Map.prototype.get ...) > - prototype > - string encoding (all strings are stored as raw binary buffer without any encoding) ================================================ FILE: runtime-library/libdyntype/dynamic-simple/context.c ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "pure_dynamic.h" /* We don't need a context for simple libdyntype implementation, but we return a * non-zero value to make the system work */ static dyn_ctx_t g_dynamic_context = (dyn_ctx_t)(uintptr_t)0xffff; /******************* Initialization and destroy *****************/ dyn_ctx_t dynamic_context_init() { return g_dynamic_context; } dyn_ctx_t dynamic_context_init_with_opt(dyn_options_t *options) { return g_dynamic_context; } void dynamic_context_destroy(dyn_ctx_t ctx) {} dyn_ctx_t dynamic_get_context() { return g_dynamic_context; } ================================================ FILE: runtime-library/libdyntype/dynamic-simple/dyn-value/README.md ================================================ # dynamic value design ![](./dyn_value.excalidraw.png) ================================================ FILE: runtime-library/libdyntype/dynamic-simple/dyn-value/class/date.c ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "dyn_class.h" #include /** * @brief char* to time_t * * @param str year-month-day hour:minute:second, like * "2023-12-23 15:58:12" * @param t time_t addr * @return int 0: success */ int strtotime(const char *str, time_t *t) { int year, month, day, hour, minute, second; struct tm tm_; if (sscanf(str, "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second) != 6) { return -1; } if (hour < 0 || hour > 23) { return -1; } tm_.tm_hour = hour; if (minute < 0 || minute > 59) { return -1; } tm_.tm_min = minute; if (second < 0 || second > 59) { return -1; } tm_.tm_sec = second; tm_.tm_year = year - 1900; tm_.tm_mon = month - 1; tm_.tm_mday = day; tm_.tm_isdst = 0; *t = mktime(&tm_); if (*t == -1) return -1; return 0; } /* Constructor (new Date()) */ DynValue * date_constructor(int argc, DynValue *argv[]) { DyntypeDate *dyn_obj = (DyntypeDate *)wasm_runtime_malloc(sizeof(DyntypeDate)); if (!dyn_obj) { return NULL; } if (!init_dyn_object((DyntypeObject *)dyn_obj, DynClassDate)) { wasm_runtime_free(dyn_obj); return NULL; } if (argc == 0) { dyn_obj->time = time(NULL); } else if (argc == 1 && argv[0]->class_id == DynClassString) { DyntypeString *str = (DyntypeString *)argv[0]; if (strtotime(str->data, &dyn_obj->time) != 0) { return NULL; } } return (DynValue *)dyn_obj; } DynValue * date_get_full_year(DynValue *this_val, int argc, DynValue *argv[]) { DyntypeDate *dyn_obj = (DyntypeDate *)this_val; struct tm *timeval = localtime(&(dyn_obj->time)); return dyn_value_new_number((double)(1900 + timeval->tm_year)); } DynValue * date_get_month(DynValue *this_val, int argc, DynValue *argv[]) { DyntypeDate *dyn_obj = (DyntypeDate *)this_val; struct tm *timeval = localtime(&(dyn_obj->time)); return dyn_value_new_number((double)(timeval->tm_mon)); } DynValue * date_get_date(DynValue *this_val, int argc, DynValue *argv[]) { DyntypeDate *dyn_obj = (DyntypeDate *)this_val; struct tm *timeval = localtime(&(dyn_obj->time)); return dyn_value_new_number((double)(timeval->tm_mday)); } DynValue * date_get_day(DynValue *this_val, int argc, DynValue *argv[]) { DyntypeDate *dyn_obj = (DyntypeDate *)this_val; struct tm *timeval = localtime(&(dyn_obj->time)); return dyn_value_new_number((double)(timeval->tm_wday)); } DynValue * date_get_hours(DynValue *this_val, int argc, DynValue *argv[]) { DyntypeDate *dyn_obj = (DyntypeDate *)this_val; struct tm *timeval = localtime(&(dyn_obj->time)); return dyn_value_new_number((double)(timeval->tm_hour)); } DynValue * date_get_minutes(DynValue *this_val, int argc, DynValue *argv[]) { DyntypeDate *dyn_obj = (DyntypeDate *)this_val; struct tm *timeval = localtime(&(dyn_obj->time)); return dyn_value_new_number((double)(timeval->tm_min)); } DynValue * date_get_seconds(DynValue *this_val, int argc, DynValue *argv[]) { DyntypeDate *dyn_obj = (DyntypeDate *)this_val; struct tm *timeval = localtime(&(dyn_obj->time)); return dyn_value_new_number((double)(timeval->tm_sec)); } /* Date.prototype.xxx */ ClassMethod date_instance_methods[] = { { "getFullYear", date_get_full_year }, { "getMonth", date_get_month }, { "getDate", date_get_date }, { "getDay", date_get_day }, { "getHours", date_get_hours }, { "getMinutes", date_get_minutes }, { "getSeconds", date_get_seconds }, }; DynValue * date_now(DynValue *this_val, int argc, DynValue *argv[]) { // get unix timestamp struct timeval start; gettimeofday(&start, NULL); return dyn_value_new_number( (double)(start.tv_sec * 1000 + start.tv_usec / 1000)); } /* Date.xxx */ ClassMethod date_class_methods[] = { { "now", date_now } }; ClassMeta date_class_meta = { .constructor = date_constructor, .parent_class_id = DynClassObject, .inst_method_num = sizeof(date_instance_methods) / sizeof(ClassMethod), .inst_methods = date_instance_methods, .class_method_num = sizeof(date_class_methods) / sizeof(ClassMethod), .class_methods = date_class_methods, .name = "Date" }; /* Date, never free this object */ DyntypeClass date_class = { .base = { .header = { .type = DynObject, .class_id = DynClassConstructor, .ref_count = 1, }, .properties = NULL, }, .meta = &date_class_meta, }; ================================================ FILE: runtime-library/libdyntype/dynamic-simple/dyn-value/class/dyn_class.c ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "dyn_class.h" /* How to add new class? */ /* * 1. Add a new file in this directory, e.g. "my_class.c" * 2. Implement the "constructor", "class methods" and "inst methods" in the * created file, refer to date.c * 3. Add corresponding entries in this file * - Class meta is mandatory, it is used to create the class instance, or * invoke the inst methods (new Date()) * - Class object is optional, it is required when you need to invoke the * class methods (e.g. Date.now()) */ /* Class meta (mandatory) */ extern ClassMeta object_class_meta; extern ClassMeta string_class_meta; extern ClassMeta date_class_meta; ClassMeta *class_meta_array[DynClassEnd] = { [DynClassNone] = NULL, [DynClassNumber] = NULL, [DynClassBoolean] = NULL, [DynClassString] = &string_class_meta, [DynClassObject] = &object_class_meta, [DynClassArray] = NULL, [DynClassExtref] = NULL, [DynClassDate] = &date_class_meta, }; /* Class object (optional) */ extern DyntypeClass object_class; extern DyntypeClass date_class; GlobalObjectEntry global_object_array[] = { { "Object", &object_class }, { "Date", &date_class }, }; /* Utilities */ static DynClassMethodCallback find_inst_method_by_classid(uint32_t class_id, const char *name) { ClassMeta *meta = NULL; int i; if (class_id >= sizeof(class_meta_array)) { return NULL; } meta = class_meta_array[class_id]; if (!meta) return NULL; for (i = 0; i < meta->inst_method_num; i++) { if (!strcmp(meta->inst_methods[i].name, name)) return meta->inst_methods[i].func; } /* Search parent inst method */ if (meta->parent_class_id != DynClassNone) { return find_inst_method_by_classid(meta->parent_class_id, name); } return NULL; } DynClassMethodCallback find_inst_method(DynValue *obj, const char *name) { if (obj->class_id == DynClassConstructor) { uint32_t i; ClassMeta *meta = ((DyntypeClass *)obj)->meta; if (!meta) return NULL; for (i = 0; i < meta->class_method_num; i++) { if (!strcmp(meta->class_methods[i].name, name)) return meta->class_methods[i].func; } } return find_inst_method_by_classid(obj->class_id, name); } DynClassConstructorCallback find_class_constructor(const char *name) { ClassMeta *meta = NULL; int i; for (i = 0; i < DynClassEnd; i++) { meta = class_meta_array[i]; if (!meta) continue; assert(meta->name != NULL); if (!strcmp(meta->name, name)) return meta->constructor; } return NULL; } DynValue * find_global_object(const char *name) { int i; for (i = 0; i < sizeof(global_object_array) / sizeof(GlobalObjectEntry); i++) { if (!strcmp(global_object_array[i].name, name)) return (DynValue *)global_object_array[i].value; } return NULL; } ================================================ FILE: runtime-library/libdyntype/dynamic-simple/dyn-value/class/dyn_class.h ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "../dyn_value.h" typedef DynValue * (*DynClassConstructorCallback)(int argc, DynValue **args); typedef DynValue * (*DynClassMethodCallback)(DynValue *self, int argc, DynValue **args); typedef struct ClassMethod { const char *name; DynClassMethodCallback func; } ClassMethod; typedef struct ClassMeta { const char *name; DynClassConstructorCallback constructor; uint16_t parent_class_id; uint16_t inst_method_num; uint16_t class_method_num; ClassMethod *inst_methods; ClassMethod *class_methods; } ClassMeta; /* A special object representing a JavaScript Class value (e.g. Object, Date, * JSON) */ typedef struct DyntypeClass { DyntypeObject base; ClassMeta *meta; } DyntypeClass; typedef struct GlobalObjectEntry { const char *name; DyntypeClass *value; } GlobalObjectEntry; DynClassMethodCallback find_inst_method(DynValue *obj, const char *name); DynClassConstructorCallback find_class_constructor(const char *name); DynValue * find_global_object(const char *name); ================================================ FILE: runtime-library/libdyntype/dynamic-simple/dyn-value/class/object.c ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "dyn_class.h" /* Constructor (new Object()) */ DynValue *object_constructor(int argc, DynValue *argv[]) { return dyn_value_new_object(); } DynValue *object_keys(DynValue *this_val, int argc, DynValue *argv[]) { return dyn_value_get_keys(argv[0]); } ClassMethod object_class_methods[] = { { "keys", object_keys }, /* TODO: add more methods */ }; ClassMeta object_class_meta = { .constructor = object_constructor, .parent_class_id = DynClassNone, .class_method_num = sizeof(object_class_methods) / sizeof(ClassMethod), .class_methods = object_class_methods, .name = "Object" }; /* Object, never free this object */ DyntypeClass object_class = { .base = { .header = { .type = DynObject, .class_id = DynClassConstructor, .ref_count = 1, }, .properties = NULL, }, .meta = &object_class_meta, }; ================================================ FILE: runtime-library/libdyntype/dynamic-simple/dyn-value/class/string.c ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "dyn_class.h" DynValue *string_concat(DynValue *this_val, int argc, DynValue *argv[]) { uint32_t i; DyntypeString *res, *this_str = (DyntypeString *)this_val; uint64_t total_string_len = this_str->length; uint64_t total_size; for (i = 0; i < argc; i++) { total_string_len += ((DyntypeString *)argv[i])->length; } total_size = total_string_len + offsetof(DyntypeString, data) + 1; if (total_size >= UINT32_MAX) { return NULL; } res = (DyntypeString *)wasm_runtime_malloc(total_size); if (!res) { return NULL; } memset(res, 0, total_size); res->header.type = DynString; res->header.ref_count = 1; res->header.class_id = DynClassString; res->length = this_str->length; bh_memcpy_s(res->data, res->length, this_str->data, this_str->length); for (i = 0; i < argc; i++) { DyntypeString *str = (DyntypeString *)argv[i]; bh_memcpy_s(res->data + res->length, str->length, str->data, str->length); res->length += str->length; } assert(res->length == total_string_len); return (DynValue *)res; } ClassMethod string_inst_methods[] = { { "concat", string_concat }, /* TODO: add more methods */ }; ClassMeta string_class_meta = { .constructor = NULL, .parent_class_id = DynClassObject, .inst_method_num = sizeof(string_inst_methods) / sizeof(ClassMethod), .inst_methods = string_inst_methods, .name = "String" }; ================================================ FILE: runtime-library/libdyntype/dynamic-simple/dyn-value/dyn_value.c ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "class/dyn_class.h" #include "libdyntype_export.h" #include "pure_dynamic.h" #define INIT_OBJ_PROPERTY_NUM 4 extern ClassMeta *class_meta_array[DynClassEnd]; static uint32_t prop_key_hash(const void *key) { return (uint32)(uintptr_t)key; } static bool prop_key_equal(void *h1, void *h2) { return strcmp(h1, h2) == 0 ? true : false; } static void prop_value_destroyer(void *value) { dynamic_release(NULL, (dyn_value_t)value); } DynValue * dyn_value_new_number(double value) { DyntypeNumber *dyn_num = (DyntypeNumber *)wasm_runtime_malloc(sizeof(DyntypeNumber)); if (!dyn_num) { return NULL; } dyn_num->header.type = DynNumber; dyn_num->header.class_id = DynClassNumber; dyn_num->header.ref_count = 1; dyn_num->value = value; return (DynValue *)dyn_num; } DynValue * dyn_value_new_boolean(bool value) { DyntypeBoolean *dyn_bool = (DyntypeBoolean *)wasm_runtime_malloc(sizeof(DyntypeBoolean)); if (!dyn_bool) { return NULL; } dyn_bool->header.type = DynBoolean; dyn_bool->header.class_id = DynClassBoolean; dyn_bool->header.ref_count = 1; dyn_bool->value = value; return (DynValue *)dyn_bool; } DynValue * dyn_value_new_string(const void *buf, uint32_t length) { uint32 total_size = offsetof(DyntypeString, data) + length + 1; DyntypeString *dyn_str = (DyntypeString *)wasm_runtime_malloc(total_size); if (!dyn_str) { return NULL; } memset(dyn_str, 0, total_size); dyn_str->header.type = DynString; dyn_str->header.class_id = DynClassString; dyn_str->header.ref_count = 1; dyn_str->length = length; bh_memcpy_s(dyn_str->data, length, buf, length); return (DynValue *)dyn_str; } DynValue * dyn_value_new_undefined() { static DynValue dyn_undefined = { .type = DynUndefined, .ref_count = 1, }; return &dyn_undefined; } DynValue * dyn_value_new_null() { static DynValue dyn_null = { .type = DynNull, .ref_count = 1, }; return &dyn_null; } bool init_dyn_object(DyntypeObject *dyn_obj, uint32_t class_id) { dyn_obj->header.type = DynObject; dyn_obj->header.class_id = class_id; dyn_obj->header.ref_count = 1; dyn_obj->properties = bh_hash_map_create( INIT_OBJ_PROPERTY_NUM, false, prop_key_hash, prop_key_equal, wasm_runtime_free, prop_value_destroyer); if (!dyn_obj->properties) { return false; } return true; } bool init_dyn_object_properties(DyntypeObject *dyn_obj) { dyn_obj->properties = bh_hash_map_create( INIT_OBJ_PROPERTY_NUM, false, prop_key_hash, prop_key_equal, wasm_runtime_free, prop_value_destroyer); if (!dyn_obj->properties) { return false; } return true; } DynValue * dyn_value_new_object() { DyntypeObject *dyn_obj = (DyntypeObject *)wasm_runtime_malloc(sizeof(DyntypeObject)); if (!dyn_obj) { return NULL; } if (!init_dyn_object(dyn_obj, DynClassObject)) { wasm_runtime_free(dyn_obj); return NULL; } return (DynValue *)dyn_obj; } DynValue * dyn_value_new_array(int len) { uint32_t total_size = offsetof(DyntypeArray, data) + len * sizeof(DynValue *); DyntypeArray *dyn_array = (DyntypeArray *)wasm_runtime_malloc(total_size); memset(dyn_array, 0, total_size); if (!init_dyn_object((DyntypeObject *)dyn_array, DynClassArray)) { wasm_runtime_free(dyn_array); return NULL; } dyn_array->length = len; return (DynValue *)dyn_array; } DynValue * dyn_value_get_global(const char *name) { DynValue *dyn_value = find_global_object(name); /* TODO: throw exception */ assert(dyn_value); dyn_value->ref_count++; return dyn_value; } DynValue * dyn_value_new_object_with_class(const char *name, int argc, DynValue **args) { DynClassConstructorCallback ctor = find_class_constructor(name); /* TODO: throw exception */ assert(ctor); return ctor(argc, args); } DynValue * dyn_value_new_extref(void *ptr, external_ref_tag tag, void *opaque) { DyntypeExtref *dyn_extref = (DyntypeExtref *)wasm_runtime_malloc(sizeof(DyntypeExtref)); if (!dyn_extref) { return NULL; } if (!init_dyn_object((DyntypeObject *)dyn_extref, DynClassExtref)) { wasm_runtime_free(dyn_extref); return NULL; } dyn_extref->tag = tag; dyn_extref->ref = (int32_t)(uintptr_t)ptr; return (DynValue *)dyn_extref; } static void object_property_counter(void *key, void *value, void *user_data) { uint32_t *counter = (uint32_t *)user_data; *counter += 1; } struct ArraySetter { dyn_ctx_t ctx; DynValue *dyn_array; uint32_t index; }; static void object_property_keys(void *key, void *value, void *user_data) { struct ArraySetter *setter_info = (struct ArraySetter *)user_data; DynValue *dyn_array = setter_info->dyn_array; DynValue *key_string = dyn_value_new_string(key, strlen(key)); uint32_t index = setter_info->index; dynamic_set_elem(NULL, dyn_array, index, key_string); /* transfer ownership to the array */ key_string->ref_count--; setter_info->index++; } DynValue * dyn_value_get_keys(DynValue *obj) { uint32_t count = 0; DyntypeObject *dyn_obj = (DyntypeObject *)obj; DyntypeArray *dyn_array = NULL; struct ArraySetter setter_info; bh_hash_map_traverse(dyn_obj->properties, object_property_counter, &count); dyn_array = (DyntypeArray *)dyn_value_new_array(count); if (!dyn_array) { return NULL; } setter_info.dyn_array = (DynValue *)dyn_array; setter_info.index = 0; bh_hash_map_traverse(dyn_obj->properties, object_property_keys, &setter_info); return (DynValue *)dyn_array; } DynValue * dyn_value_invoke(DynValue *obj, const char *name, int argc, DynValue **args) { DynClassMethodCallback f = find_inst_method(obj, name); /* TODO: throw exception */ assert(f); return f(obj, argc, args); } DynValue * dyn_value_hold(DynValue *obj) { DynValue *dyn_value = (DynValue *)obj; dyn_value->ref_count++; return obj; } static void dyn_value_destroy(void *obj) { DynValue *dyn_value = (DynValue *)obj; if (dyn_value->type == DynObject) { bh_hash_map_destroy(((DyntypeObject *)dyn_value)->properties); if (dyn_value->class_id == DynClassArray) { uint32 i; DyntypeArray *arr = (DyntypeArray *)dyn_value; for (i = 0; i < arr->length; i++) { if (arr->data[i]) { dyn_value_release(arr->data[i]); } } } } wasm_runtime_free(dyn_value); } void dyn_value_release(DynValue *obj) { DynValue *dyn_value = (DynValue *)obj; if (dyn_value->type == DynUndefined || dyn_value->type == DynNull) { return; } dyn_value->ref_count--; if (dyn_value->ref_count == 0) { dyn_value_destroy(obj); } } /* string utilities */ DyntypeString * dyn_string_concat(DyntypeString *dyn_str1, DyntypeString *dyn_str2) { uint32_t total_size = offsetof(DyntypeString, data) + dyn_str1->length + dyn_str2->length + 1; DyntypeString *dyn_str = (DyntypeString *)wasm_runtime_malloc(total_size); if (!dyn_str) { return NULL; } memset(dyn_str, 0, total_size); dyn_str->header.type = DynString; dyn_str->header.ref_count = 1; dyn_str->header.class_id = DynClassString; dyn_str->length = dyn_str1->length + dyn_str2->length; bh_memcpy_s(dyn_str->data, dyn_str1->length, dyn_str1->data, dyn_str1->length); bh_memcpy_s(dyn_str->data + dyn_str1->length, dyn_str2->length, dyn_str2->data, dyn_str2->length); return dyn_str; } int32_t dyn_string_eq(DyntypeString *dyn_str1, DyntypeString *dyn_str2) { if (dyn_str1 == dyn_str2) { return true; } if (dyn_str1->length != dyn_str2->length) { return false; } return strcmp(dyn_str1->data, dyn_str2->data) == 0 ? true : false; } DyntypeString * dyn_string_slice(DyntypeString *dyn_str, uint32_t start, uint32_t end) { uint32_t total_size, actual_end; DyntypeString *dyn_str_res = NULL; actual_end = end == UINT32_MAX ? dyn_str->length : end; total_size = offsetof(DyntypeString, data) + actual_end - start + 1; dyn_str_res = (DyntypeString *)wasm_runtime_malloc(total_size); if (!dyn_str_res) { return NULL; } memset(dyn_str_res, 0, total_size); dyn_str_res->header.type = DynString; dyn_str_res->header.ref_count = 1; dyn_str->header.class_id = DynClassString; dyn_str_res->length = actual_end - start; bh_memcpy_s(dyn_str_res->data, dyn_str_res->length, dyn_str->data + start, dyn_str_res->length); return dyn_str_res; } ================================================ FILE: runtime-library/libdyntype/dynamic-simple/dyn-value/dyn_value.h ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "bh_platform.h" #include "libdyntype.h" enum DynValueClass { DynClassNone = 0, /* For undefined and null */ DynClassConstructor = 1, DynClassNumber = 10, DynClassBoolean, DynClassString, DynClassObject, DynClassArray, DynClassExtref, DynClassDate, DynClassEnd, }; typedef struct DynValue { uint8_t type; uint8_t class_id; uint16_t ref_count; } DynValue; typedef struct DyntypeNumber { DynValue header; double value; } DyntypeNumber; typedef struct DyntypeBoolean { DynValue header; bool value; } DyntypeBoolean; typedef struct DyntypeString { DynValue header; uint32_t length; uint8_t data[1]; } DyntypeString; typedef struct DyntypeObject { DynValue header; HashMap *properties; } DyntypeObject; typedef struct DyntypeArray { DyntypeObject base; uint32_t length; DynValue *data[1]; } DyntypeArray; typedef struct DyntypeExtref { DyntypeObject base; int32_t tag; int32_t ref; } DyntypeExtref; typedef struct DyntypeDate { DyntypeObject base; time_t time; } DyntypeDate; DynValue * dyn_value_new_number(double value); DynValue * dyn_value_new_boolean(bool value); DynValue * dyn_value_new_string(const void *buf, uint32_t length); DynValue * dyn_value_new_undefined(); DynValue * dyn_value_new_null(); bool init_dyn_object(DyntypeObject *dyn_obj, uint32_t class_id); bool init_dyn_object_properties(DyntypeObject *dyn_obj); DynValue * dyn_value_new_object(); DynValue * dyn_value_new_array(int len); DynValue * dyn_value_get_global(const char *name); DynValue * dyn_value_new_object_with_class(const char *name, int argc, DynValue **args); DynValue * dyn_value_new_extref(void *ptr, external_ref_tag tag, void *opaque); DynValue * dyn_value_get_keys(DynValue *dyn_obj); DynValue * dyn_value_invoke(DynValue *obj, const char *name, int argc, DynValue **args); DynValue * dyn_value_hold(DynValue *obj); void dyn_value_release(DynValue *obj); /* string utilities */ DyntypeString * dyn_string_concat(DyntypeString *dyn_str1, DyntypeString *dyn_str2); int32_t dyn_string_eq(DyntypeString *dyn_str1, DyntypeString *dyn_str2); DyntypeString * dyn_string_slice(DyntypeString *dyn_str, uint32_t start, uint32_t end); ================================================ FILE: runtime-library/libdyntype/dynamic-simple/fallback.c ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "pure_dynamic.h" #include "dyn_value.h" #include /******************* function fallback *******************/ dyn_value_t dynamic_invoke(dyn_ctx_t ctx, const char *name, dyn_value_t obj, int argc, dyn_value_t *args) { return dyn_value_invoke(obj, name, argc, (DynValue **)args); } int dynamic_execute_pending_jobs(dyn_ctx_t ctx) { return 0; } ================================================ FILE: runtime-library/libdyntype/dynamic-simple/object.c ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "libdyntype_export.h" #include "pure_dynamic.h" #include "dyn_value.h" #include /******************* builtin type compare *******************/ static inline bool number_cmp(double lhs, double rhs, cmp_operator operator_kind) { bool res = false; switch (operator_kind) { case LessThanToken: { res = lhs < rhs; break; } case GreaterThanToken: { res = lhs > rhs; break; } case EqualsEqualsToken: case EqualsEqualsEqualsToken: { res = lhs == rhs; break; } case LessThanEqualsToken: { res = lhs <= rhs; break; } case GreaterThanEqualsToken: { res = lhs >= rhs; break; } case ExclamationEqualsToken: case ExclamationEqualsEqualsToken: { res = lhs != rhs; break; } } return res; } static inline bool string_cmp(const char *lhs, const char *rhs, cmp_operator operator_kind) { bool res = false; int cmp_res = strcmp(lhs, rhs); switch (operator_kind) { case LessThanToken: { res = cmp_res < 0; break; } case GreaterThanToken: { res = cmp_res > 0; break; } case EqualsEqualsToken: case EqualsEqualsEqualsToken: { res = cmp_res == 0; break; } case LessThanEqualsToken: { res = cmp_res <= 0; break; } case GreaterThanEqualsToken: { res = cmp_res >= 0; break; } case ExclamationEqualsToken: case ExclamationEqualsEqualsToken: { res = cmp_res != 0; break; } } return res; } static inline bool bool_cmp(bool lhs, bool rhs, cmp_operator operator_kind) { bool res = false; switch (operator_kind) { case LessThanToken: { res = lhs < rhs; break; } case GreaterThanToken: { res = lhs > rhs; break; } case EqualsEqualsToken: case EqualsEqualsEqualsToken: { res = lhs == rhs; break; } case LessThanEqualsToken: { res = lhs <= rhs; break; } case GreaterThanEqualsToken: { res = lhs >= rhs; break; } case ExclamationEqualsToken: case ExclamationEqualsEqualsToken: { res = lhs != rhs; break; } } return res; } static inline bool cmp_operator_has_equal_token(cmp_operator operator_kind) { if (operator_kind == EqualsEqualsToken || operator_kind == EqualsEqualsEqualsToken || operator_kind == LessThanEqualsToken || operator_kind == GreaterThanEqualsToken) { return true; } return false; } /******************* Field access *******************/ dyn_value_t dynamic_new_number(dyn_ctx_t ctx, double value) { return dyn_value_new_number(value); } dyn_value_t dynamic_new_boolean(dyn_ctx_t ctx, bool value) { return dyn_value_new_boolean(value); } dyn_value_t dynamic_new_string(dyn_ctx_t ctx, const void *stringref) { DynValue *dyn_value = (DynValue *)stringref; dyn_value->ref_count++; return (dyn_value_t)stringref; } dyn_value_t dynamic_new_undefined(dyn_ctx_t ctx) { return dyn_value_new_undefined(); } dyn_value_t dynamic_new_null(dyn_ctx_t ctx) { return dyn_value_new_null(); } dyn_value_t dynamic_new_object(dyn_ctx_t ctx) { return dyn_value_new_object(); } dyn_value_t dynamic_new_array(dyn_ctx_t ctx, int len) { return dyn_value_new_array(len); } dyn_value_t dynamic_get_global(dyn_ctx_t ctx, const char *name) { return dyn_value_get_global(name); } dyn_value_t dynamic_new_object_with_class(dyn_ctx_t ctx, const char *name, int argc, dyn_value_t *args) { return dyn_value_new_object_with_class(name, argc, (DynValue **)args); } dyn_value_t dynamic_new_extref(dyn_ctx_t ctx, void *ptr, external_ref_tag tag, void *opaque) { return dyn_value_new_extref(ptr, tag, opaque); } int dynamic_set_elem(dyn_ctx_t ctx, dyn_value_t obj, int index, dyn_value_t elem) { DyntypeArray *dyn_array = (DyntypeArray *)obj; if (dyn_array->base.header.type != DynObject || dyn_array->base.header.class_id != DynClassArray) { return false; } if (index < 0 || index >= dyn_array->length) { return false; } if (dyn_array->data[index]) { dynamic_release(ctx, dyn_array->data[index]); } dyn_array->data[index] = elem; dynamic_hold(ctx, elem); return true; } dyn_value_t dynamic_get_elem(dyn_ctx_t ctx, dyn_value_t obj, int index) { DyntypeArray *dyn_array = (DyntypeArray *)obj; if (dyn_array->base.header.type != DynObject || dyn_array->base.header.class_id != DynClassArray) { return NULL; } if (index < 0 || index >= dyn_array->length) { return NULL; } if (!dyn_array->data[index]) { return dynamic_new_undefined(ctx); } ((DynValue *)dyn_array->data[index])->ref_count++; return dyn_array->data[index]; } int dynamic_set_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop, dyn_value_t value) { DyntypeObject *dyn_obj = (DyntypeObject *)obj; char *key = NULL; if (dyn_obj->header.type != DynObject) { return false; } if (!dyn_obj->properties) { if (!init_dyn_object_properties(dyn_obj)) { return false; } } key = bh_strdup(prop); if (!bh_hash_map_find(dyn_obj->properties, (void *)key)) { if (!bh_hash_map_insert(dyn_obj->properties, (void *)key, value)) { wasm_runtime_free(key); return false; } dynamic_hold(ctx, value); } else { void *old_key; DynValue *old_value; bh_hash_map_remove(dyn_obj->properties, (void *)key, &old_key, (void **)&old_value); wasm_runtime_free(old_key); if (!bh_hash_map_insert(dyn_obj->properties, (void *)key, value)) { wasm_runtime_free(key); return false; } dynamic_hold(ctx, value); dynamic_release(ctx, old_value); } return true; } int dynamic_define_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop, dyn_value_t desc) { return 0; } dyn_value_t dynamic_get_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop) { DyntypeObject *dyn_obj = (DyntypeObject *)obj; DynValue *dyn_value = NULL; if (dyn_obj->header.type != DynObject) { return NULL; } if (!dyn_obj->properties) { return NULL; } if (dyn_obj->header.class_id == DynClassArray && strcmp(prop, "length") == 0) { DyntypeArray *dyn_array = (DyntypeArray *)dyn_obj; return dynamic_new_number(ctx, dyn_array->length); } dyn_value = bh_hash_map_find(dyn_obj->properties, (void *)prop); if (dyn_value) { dyn_value->ref_count++; } else { return dynamic_new_undefined(ctx); } return dyn_value; } int dynamic_has_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop) { DyntypeObject *dyn_obj = (DyntypeObject *)obj; if (dyn_obj->header.type != DynObject) { return false; } if (!dyn_obj->properties) { return false; } if (bh_hash_map_find(dyn_obj->properties, (void *)prop)) { return true; } return false; } int dynamic_delete_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop) { DyntypeObject *dyn_obj = (DyntypeObject *)obj; void *orig_key; DynValue *value = NULL; if (dyn_obj->header.type != DynObject) { return false; } if (!dyn_obj->properties) { return false; } if (!bh_hash_map_remove(dyn_obj->properties, (void *)prop, &orig_key, (void **)&value)) { return false; } dynamic_release(ctx, value); wasm_runtime_free(orig_key); return true; } dyn_value_t dynamic_get_keys(dyn_ctx_t ctx, dyn_value_t obj) { return dyn_value_get_keys(obj); } /******************* Runtime type checking *******************/ bool dynamic_is_undefined(dyn_ctx_t ctx, dyn_value_t obj) { DynValue *dyn_value = (DynValue *)obj; return dyn_value->type == DynUndefined ? true : false; } bool dynamic_is_null(dyn_ctx_t ctx, dyn_value_t obj) { DynValue *dyn_value = (DynValue *)obj; return dyn_value->type == DynNull ? true : false; } bool dynamic_is_bool(dyn_ctx_t ctx, dyn_value_t obj) { DynValue *dyn_value = (DynValue *)obj; return dyn_value->type == DynBoolean ? true : false; } int dynamic_to_bool(dyn_ctx_t ctx, dyn_value_t bool_obj, bool *pres) { DynValue *dyn_value = (DynValue *)bool_obj; DyntypeBoolean *dyn_bool = (DyntypeBoolean *)dyn_value; if (dyn_value->type != DynBoolean) { return -DYNTYPE_TYPEERR; } *pres = dyn_bool->value; return DYNTYPE_SUCCESS; } bool dynamic_is_number(dyn_ctx_t ctx, dyn_value_t obj) { DynValue *dyn_value = (DynValue *)obj; return dyn_value->type == DynNumber ? true : false; } int dynamic_to_number(dyn_ctx_t ctx, dyn_value_t obj, double *pres) { DynValue *dyn_value = (DynValue *)obj; DyntypeNumber *dyn_num = (DyntypeNumber *)dyn_value; if (dyn_value->type != DynNumber) { return -DYNTYPE_EXCEPTION; } *pres = dyn_num->value; return DYNTYPE_SUCCESS; } bool dynamic_is_string(dyn_ctx_t ctx, dyn_value_t obj) { DynValue *dyn_value = (DynValue *)obj; return dyn_value->type == DynString ? true : false; } void * dynamic_to_string(dyn_ctx_t ctx, dyn_value_t obj) { DynValue *dyn_value = (DynValue *)obj; dyn_value->ref_count++; return obj; } int dynamic_to_cstring(dyn_ctx_t ctx, dyn_value_t str_obj, char **pres) { DynValue *dyn_value = (DynValue *)str_obj; switch (dyn_value->type) { case DynString: { DyntypeString *dyn_str = (DyntypeString *)dyn_value; *pres = wasm_runtime_malloc(dyn_str->length + 1); if (!*pres) { return -DYNTYPE_EXCEPTION; } bh_memcpy_s(*pres, dyn_str->length + 1, dyn_str->data, dyn_str->length + 1); break; } case DynNumber: { DyntypeNumber *dyn_num = (DyntypeNumber *)dyn_value; double value = dyn_num->value; char buf[128]; if (value - (int64_t)value != 0) { snprintf(buf, sizeof(buf), "%.14g", value); } else { snprintf(buf, sizeof(buf), "%"PRId64, (int64_t)value); } *pres = bh_strdup(buf); if (!*pres) { return -DYNTYPE_EXCEPTION; } break; } case DynBoolean: { DyntypeBoolean *dyn_bool = (DyntypeBoolean *)dyn_value; *pres = bh_strdup(dyn_bool->value ? "true" : "false"); if (!*pres) { return -DYNTYPE_EXCEPTION; } break; } case DynUndefined: { *pres = bh_strdup("undefined"); if (!*pres) { return -DYNTYPE_EXCEPTION; } break; } case DynNull: { *pres = bh_strdup("null"); if (!*pres) { return -DYNTYPE_EXCEPTION; } break; } case DynObject: { *pres = bh_strdup("[object Object]"); if (!*pres) { return -DYNTYPE_EXCEPTION; } break; } default: { return -DYNTYPE_EXCEPTION; } } return DYNTYPE_SUCCESS; } void dynamic_free_cstring(dyn_ctx_t ctx, char *str) { wasm_runtime_free(str); } bool dynamic_is_object(dyn_ctx_t ctx, dyn_value_t obj) { DynValue *dyn_value = (DynValue *)obj; return dyn_value->type == DynObject ? true : false; } bool dynamic_is_function(dyn_ctx_t ctx, dyn_value_t obj) { return false; } bool dynamic_is_array(dyn_ctx_t ctx, dyn_value_t obj) { DynValue *dyn_value = (DynValue *)obj; return ((dyn_value->type == DynObject) && (dyn_value->class_id == DynClassArray)) ? true : false; } bool dynamic_is_extref(dyn_ctx_t ctx, dyn_value_t obj) { DynValue *dyn_value = (DynValue *)obj; return (dyn_value->type == DynObject) && (dyn_value->class_id == DynClassExtref) ? true : false; } int dynamic_to_extref(dyn_ctx_t ctx, dyn_value_t obj, void **pres) { DynValue *dyn_value = (DynValue *)obj; DyntypeExtref *dyn_extref = (DyntypeExtref *)dyn_value; if (dyn_value->type != DynObject || dyn_value->class_id != DynClassExtref) { return -DYNTYPE_TYPEERR; } *pres = (void *)(uintptr_t)dyn_extref->ref; return dyn_extref->tag; } bool dynamic_is_exception(dyn_ctx_t ctx, dyn_value_t value) { return false; } bool dynamic_is_falsy(dyn_ctx_t ctx, dyn_value_t value) { DynValue *obj = (DynValue *)value; if (obj->type == DynUndefined || obj->type == DynNull || (obj->type == DynBoolean && !((DyntypeBoolean *)obj)->value) || (obj->type == DynNumber && !((DyntypeNumber *)obj)->value) || (obj->type == DynString && !((DyntypeString *)obj)->length)) { return true; } return false; } /******************* Type equivalence *******************/ dyn_type_t dynamic_typeof(dyn_ctx_t ctx, dyn_value_t obj) { DynValue *dyn_value = (DynValue *)obj; if (dyn_value->type == DynObject && dyn_value->class_id == DynClassExtref) { DyntypeExtref *extref_value = (DyntypeExtref *)dyn_value; if (extref_value->tag == ExtObj) { return DynExtRefObj; } else if (extref_value->tag == ExtFunc) { return DynExtRefFunc; } else if (extref_value->tag == ExtArray) { return DynExtRefArray; } } return ((DynValue *)obj)->type; } bool dynamic_type_eq(dyn_ctx_t ctx, dyn_value_t lhs, dyn_value_t rhs) { return ((DynValue *)lhs)->type == ((DynValue *)rhs)->type ? true : false; } bool dynamic_cmp(dyn_ctx_t ctx, dyn_value_t lhs, dyn_value_t rhs, cmp_operator operator_kind) { bool res; dyn_type_t type; if (lhs == rhs) { if (cmp_operator_has_equal_token(operator_kind)) { return true; } else { return false; } } type = dynamic_typeof(ctx, lhs); switch (type) { case DynBoolean: { bool lhs_b = 0, rhs_b = 0; dynamic_to_bool(ctx, lhs, &lhs_b); dynamic_to_bool(ctx, rhs, &rhs_b); res = bool_cmp(lhs_b, rhs_b, operator_kind); break; } case DynNumber: { double lhs_n = 0, rhs_n = 0; dynamic_to_number(ctx, lhs, &lhs_n); dynamic_to_number(ctx, rhs, &rhs_n); res = number_cmp(lhs_n, rhs_n, operator_kind); break; } case DynNull: { if (cmp_operator_has_equal_token(operator_kind)) { res = true; } else { res = false; } break; } case DynUndefined: { /** undefined <= undefined => false*/ if (operator_kind == EqualsEqualsToken || operator_kind == EqualsEqualsEqualsToken) { res = true; } else { res = false; } break; } case DynString: { char *lhs_s, *rhs_s; dynamic_to_cstring(ctx, lhs, &lhs_s); dynamic_to_cstring(ctx, rhs, &rhs_s); res = string_cmp(lhs_s, rhs_s, operator_kind); dynamic_free_cstring(ctx, lhs_s); dynamic_free_cstring(ctx, rhs_s); break; } case DynObject: { /** only allows == / === / != / !== */ if (operator_kind < EqualsEqualsToken) { printf("[runtime library error]: non-equal compare token on " "two any type objects"); } res = lhs == rhs; if (operator_kind == ExclamationEqualsToken || operator_kind == ExclamationEqualsEqualsToken) { res = !res; } break; } default: { res = false; } } return res; } /******************* Subtyping *******************/ dyn_value_t dynamic_new_object_with_proto(dyn_ctx_t ctx, const dyn_value_t proto_obj) { return NULL; } int dynamic_set_prototype(dyn_ctx_t ctx, dyn_value_t obj, const dyn_value_t proto_obj) { return 0; } dyn_value_t dynamic_get_prototype(dyn_ctx_t ctx, dyn_value_t obj) { return NULL; } dyn_value_t dynamic_get_own_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop) { return NULL; } bool dynamic_instanceof(dyn_ctx_t ctx, const dyn_value_t src_obj, const dyn_value_t dst_obj) { return false; } /******************* Dumping *******************/ void dynamic_dump_value(dyn_ctx_t ctx, dyn_value_t obj) { DynValue *dyn_value = (DynValue *)obj; switch (dyn_value->type) { case DynUndefined: { printf("undefined"); break; } case DynNull: { printf("null"); break; } case DynBoolean: { printf("%s", ((DyntypeBoolean *)dyn_value)->value ? "true" : "false"); break; } case DynNumber: { double value = ((DyntypeNumber *)dyn_value)->value; if (value - (int64_t)value != 0) { printf("%.14g", value); } else { printf("%"PRId64, (uint64_t)value); } break; } case DynString: { printf("%s", ((DyntypeString *)dyn_value)->data); break; } case DynObject: { switch (dyn_value->class_id) { case DynClassArray: { uint32 i; DyntypeArray *arr = (DyntypeArray *)dyn_value; printf("["); for (i = 0; i < arr->length; i++) { if (arr->data[i]) { dynamic_dump_value(ctx, arr->data[i]); } else { printf("undefined"); } if (i < arr->length - 1) { printf(", "); } } printf("]"); break; } case DynClassExtref: { printf("[object WasmObject]"); break; } default: { printf("[object Object]"); break; } } break; } default: { printf("[unknown type]"); } } } int dynamic_dump_value_buffer(dyn_ctx_t ctx, dyn_value_t obj, void *buffer, int len) { return 0; } void dynamic_dump_error(dyn_ctx_t ctx) {} /******************* Garbage collection *******************/ dyn_value_t dynamic_hold(dyn_ctx_t ctx, dyn_value_t obj) { return dyn_value_hold(obj); } void dynamic_release(dyn_ctx_t ctx, dyn_value_t obj) { dyn_value_release(obj); } void dynamic_collect(dyn_ctx_t ctx) { // TODO } /******************* Exception *******************/ dyn_value_t dynamic_throw_exception(dyn_ctx_t ctx, dyn_value_t obj) { return NULL; } /******************* Special Property Access *******************/ int dynamic_get_array_length(dyn_ctx_t ctx, dyn_value_t obj) { DyntypeArray *dyn_arr = (DyntypeArray *)obj; return dyn_arr->length; } ================================================ FILE: runtime-library/libdyntype/dynamic-simple/pure_dynamic.h ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #ifndef __PURE_DYNAMIC_H_ #define __PURE_DYNAMIC_H_ #include "libdyntype.h" #ifdef __cplusplus extern "C" { #endif /********************************************/ /* APIs exposed to runtime embedder */ /********************************************/ dyn_ctx_t dynamic_context_init(); dyn_ctx_t dynamic_context_init_with_opt(dyn_options_t *options); void dynamic_context_destroy(dyn_ctx_t ctx); int dynamic_execute_pending_jobs(dyn_ctx_t ctx); void dynamic_dump_error(dyn_ctx_t ctx); dyn_value_t dynamic_throw_exception(dyn_ctx_t ctx, dyn_value_t obj); void dynamic_dump_value(dyn_ctx_t ctx, dyn_value_t obj); int dynamic_dump_value_buffer(dyn_ctx_t ctx, dyn_value_t obj, void *buffer, int len); dyn_value_t dynamic_hold(dyn_ctx_t ctx, dyn_value_t obj); void dynamic_release(dyn_ctx_t ctx, dyn_value_t obj); void dynamic_collect(dyn_ctx_t ctx); /********************************************/ /* APIs exposed to wasm application */ /********************************************/ dyn_ctx_t dynamic_get_context(); dyn_value_t dynamic_new_number(dyn_ctx_t ctx, double value); dyn_value_t dynamic_new_boolean(dyn_ctx_t ctx, bool value); #if WASM_ENABLE_STRINGREF != 0 dyn_value_t dynamic_new_string(dyn_ctx_t ctx, const void *stringref); #else dyn_value_t dynamic_new_string(dyn_ctx_t ctx, const char *str, int len); #endif dyn_value_t dynamic_new_undefined(dyn_ctx_t ctx); dyn_value_t dynamic_new_null(dyn_ctx_t ctx); dyn_value_t dynamic_new_object(dyn_ctx_t ctx); dyn_value_t dynamic_new_object_with_proto(dyn_ctx_t ctx, const dyn_value_t proto_obj); dyn_value_t dynamic_new_object_with_class(dyn_ctx_t ctx, const char *name, int argc, dyn_value_t *args); dyn_value_t dynamic_new_array(dyn_ctx_t ctx, int len); dyn_value_t dynamic_new_extref(dyn_ctx_t ctx, void *ptr, external_ref_tag tag, void *opaque); int dynamic_set_elem(dyn_ctx_t ctx, dyn_value_t obj, int index, dyn_value_t elem); dyn_value_t dynamic_get_elem(dyn_ctx_t ctx, dyn_value_t obj, int index); int dynamic_set_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop, dyn_value_t value); dyn_value_t dynamic_get_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop); dyn_value_t dynamic_get_own_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop); int dynamic_define_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop, dyn_value_t desc); int dynamic_has_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop); int dynamic_delete_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop); bool dynamic_is_number(dyn_ctx_t ctx, dyn_value_t obj); int dynamic_to_number(dyn_ctx_t ctx, dyn_value_t obj, double *pres); bool dynamic_is_bool(dyn_ctx_t ctx, dyn_value_t obj); int dynamic_to_bool(dyn_ctx_t ctx, dyn_value_t bool_obj, bool *pres); bool dynamic_is_string(dyn_ctx_t ctx, dyn_value_t obj); #if WASM_ENABLE_STRINGREF != 0 void * dynamic_to_string(dyn_ctx_t ctx, dyn_value_t obj); #endif int dynamic_to_cstring(dyn_ctx_t ctx, dyn_value_t str_obj, char **pres); void dynamic_free_cstring(dyn_ctx_t ctx, char *str); bool dynamic_is_undefined(dyn_ctx_t ctx, dyn_value_t obj); bool dynamic_is_null(dyn_ctx_t ctx, dyn_value_t obj); bool dynamic_is_object(dyn_ctx_t ctx, dyn_value_t obj); bool dynamic_is_function(dyn_ctx_t ctx, dyn_value_t obj); bool dynamic_is_array(dyn_ctx_t ctx, dyn_value_t obj); bool dynamic_is_extref(dyn_ctx_t ctx, dyn_value_t obj); int dynamic_to_extref(dyn_ctx_t ctx, dyn_value_t obj, void **pres); bool dynamic_is_exception(dyn_ctx_t ctx, dyn_value_t value); bool dynamic_is_falsy(dyn_ctx_t ctx, dyn_value_t value); dyn_type_t dynamic_typeof(dyn_ctx_t ctx, dyn_value_t obj); bool dynamic_type_eq(dyn_ctx_t ctx, dyn_value_t lhs, dyn_value_t rhs); bool dynamic_cmp(dyn_ctx_t ctx, dyn_value_t lhs, dyn_value_t rhs, cmp_operator operator_kind); int dynamic_set_prototype(dyn_ctx_t ctx, dyn_value_t obj, const dyn_value_t proto_obj); dyn_value_t dynamic_get_prototype(dyn_ctx_t ctx, dyn_value_t obj); bool dynamic_instanceof(dyn_ctx_t ctx, const dyn_value_t src_obj, const dyn_value_t dst_obj); dyn_value_t dynamic_invoke(dyn_ctx_t ctx, const char *name, dyn_value_t this_obj, int argc, dyn_value_t *args); dyn_value_t dynamic_get_global(dyn_ctx_t ctx, const char *name); dyn_value_t dynamic_get_keys(dyn_ctx_t ctx, dyn_value_t obj); /******************* Special Property Access *******************/ int dynamic_get_array_length(dyn_ctx_t ctx, dyn_value_t obj); #ifdef __cplusplus } #endif #endif /* end of __PURE_DYNAMIC_H_ */ ================================================ FILE: runtime-library/libdyntype/extref/extref.c ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "extref.h" #include "pure_dynamic.h" #include "libdyntype_export.h" #include "bh_assert.h" #include "bh_common.h" #include "gc_export.h" #include "wamr_utils.h" #include "type_utils.h" #include "object_utils.h" #define EXTREF_PROLOGUE() \ int ext_tag; \ void *p_table_index; \ uint32_t table_index; \ wasm_exec_env_t exec_env = dyntype_context_get_exec_env(); \ wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); \ \ bh_assert(exec_env); \ \ ext_tag = dynamic_to_extref(ctx, obj, &p_table_index); \ table_index = (uint32_t)(uintptr_t)p_table_index; int extref_set_elem(dyn_ctx_t ctx, dyn_value_t obj, int index, dyn_value_t elem) { EXTREF_PROLOGUE() if (ext_tag == ExtArray) { /* Get static array info */ WasmArrayInfo arr_info; wasm_value_t unboxed_elem_value = { 0 }; get_static_array_info(exec_env, table_index, &arr_info); /* unbox value from any */ unbox_value_from_any(exec_env, ctx, elem, arr_info.element_type, &unboxed_elem_value, false, -1); /* set value to array */ wasm_array_obj_set_elem(arr_info.ref, index, &unboxed_elem_value); return DYNTYPE_SUCCESS; } else { wasm_runtime_set_exception( module_inst, "libdyntype: set element on non-array object"); return -DYNTYPE_TYPEERR; } } dyn_value_t extref_get_elem(dyn_ctx_t ctx, dyn_value_t obj, int index) { EXTREF_PROLOGUE() if (ext_tag == ExtArray) { /* Get static array info */ WasmArrayInfo arr_info; wasm_value_t elem_value = { 0 }; dyn_value_t elem_res_any = NULL; get_static_array_info(exec_env, table_index, &arr_info); /* get value from array */ wasm_array_obj_get_elem(arr_info.ref, index, false, &elem_value); /* unbox value from any */ elem_res_any = box_value_to_any(exec_env, ctx, &elem_value, arr_info.element_type, false, -1); return elem_res_any; } else { wasm_runtime_set_exception( module_inst, "libdyntype: get element on non-array object"); return NULL; } } int extref_set_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop, dyn_value_t value) { EXTREF_PROLOGUE() if (ext_tag == ExtObj) { int index; wasm_ref_type_t field_type; wasm_obj_t wasm_obj = (wasm_obj_t)wamr_utils_get_table_element(exec_env, table_index); wasm_value_t wasm_value = { .gc_obj = wasm_obj }; bh_assert(wasm_obj_is_struct_obj(wasm_obj)); index = get_prop_index_of_struct(exec_env, prop, &wasm_obj, &field_type); if (index < 0) { return dynamic_set_property(ctx, obj, prop, value); } else { unbox_value_from_any(exec_env, ctx, value, field_type, &wasm_value, true, index); return DYNTYPE_SUCCESS; } } else if (ext_tag == ExtArray) { wasm_runtime_set_exception(module_inst, "libdyntype: set property on non-object"); } return -DYNTYPE_TYPEERR; } dyn_value_t extref_get_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop) { EXTREF_PROLOGUE() if (ext_tag == ExtObj) { int index; wasm_ref_type_t field_type; wasm_obj_t wasm_obj = (wasm_obj_t)wamr_utils_get_table_element(exec_env, table_index); bh_assert(wasm_obj_is_struct_obj(wasm_obj)); index = get_prop_index_of_struct(exec_env, prop, &wasm_obj, &field_type); if (index < 0) { return dynamic_get_property(ctx, obj, prop); } else { wasm_value_t field_wasm_value = { .gc_obj = wasm_obj }; return box_value_to_any(exec_env, ctx, &field_wasm_value, field_type, true, index); } } else if (ext_tag == ExtArray) { WasmArrayInfo arr_info; if (strcmp(prop, "length") != 0) { wasm_runtime_set_exception( module_inst, "libdyntype: get property on non-object"); return NULL; } get_static_array_info(exec_env, table_index, &arr_info); return dynamic_new_number(ctx, (double)arr_info.lengh); } return NULL; } dyn_value_t extref_get_own_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop) { return extref_get_property(ctx, obj, prop); } int extref_has_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop) { EXTREF_PROLOGUE() if (ext_tag == ExtObj) { int index; wasm_ref_type_t field_type; wasm_obj_t wasm_obj = (wasm_obj_t)wamr_utils_get_table_element(exec_env, table_index); bh_assert(wasm_obj_is_struct_obj(wasm_obj)); index = get_prop_index_of_struct(exec_env, prop, &wasm_obj, &field_type); if (index < 0) { return dynamic_has_property(ctx, obj, prop); } else { return DYNTYPE_TRUE; } } else { wasm_runtime_set_exception(module_inst, "libdyntype: check property on non-object"); } return DYNTYPE_FALSE; } int extref_delete_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop) { EXTREF_PROLOGUE() if (ext_tag == ExtObj) { int index; wasm_ref_type_t field_type; wasm_obj_t wasm_obj = (wasm_obj_t)wamr_utils_get_table_element(exec_env, table_index); bh_assert(wasm_obj_is_struct_obj(wasm_obj)); index = get_prop_index_of_struct(exec_env, prop, &wasm_obj, &field_type); if (index < 0) { return dynamic_delete_property(ctx, obj, prop); } wasm_runtime_set_exception( module_inst, "libdyntype: delete property on static type object"); } else { wasm_runtime_set_exception(module_inst, "libdyntype: delete property on non-object"); } return DYNTYPE_FALSE; } dyn_value_t extref_invoke(dyn_ctx_t ctx, const char *name, dyn_value_t obj, int argc, dyn_value_t *args) { dyn_value_t field_any_obj = NULL; dyn_value_t res = NULL; EXTREF_PROLOGUE() if (ext_tag == ExtObj) { /* invoke method of static typed object */ bh_assert(name); field_any_obj = dyntype_get_property(ctx, obj, name); /* the method property has been boxed to newExtref, need to unbox to * get the real ptr */ bh_assert(dyntype_is_extref(ctx, field_any_obj)); ext_tag = dynamic_to_extref(ctx, field_any_obj, (void **)&p_table_index); table_index = (uint32_t)(uintptr_t)p_table_index; } if (ext_tag == ExtFunc) { /* invoke static closure */ wasm_obj_t func_obj = (wasm_obj_t)wamr_utils_get_table_element(exec_env, table_index); bh_assert(wasm_obj_is_struct_obj(func_obj)); res = call_wasm_func_with_boxing( exec_env, ctx, (wasm_anyref_obj_t)func_obj, argc, args); } else { wasm_runtime_set_exception(module_inst, "libdyntype: invoke on non-function"); } if (field_any_obj) { /* field_any_obj doesn't returned to wasm side, it's not managed by * WasmGC, must be released manually */ dyntype_release(ctx, field_any_obj); } return res; } dyn_value_t extref_get_keys(dyn_ctx_t ctx, dyn_value_t obj) { dyn_value_t arr = NULL, str = NULL; void *meta_addr = NULL; uint32_t prop_count = 0, i = 0, iter_prop_count = 0; char *prop_name = NULL; char **prop_name_list = NULL; EXTREF_PROLOGUE() if (ext_tag == ExtObj) { wasm_obj_t obj_struct = (wasm_obj_t)wamr_utils_get_table_element(exec_env, table_index); /* get meta, get prop names */ meta_addr = get_meta_of_object(exec_env, obj_struct); prop_count = get_meta_fields_count(meta_addr); if (prop_count > 0) { prop_name_list = wasm_runtime_malloc(prop_count * sizeof(char *)); if (!prop_name_list) { wasm_runtime_set_exception(module_inst, "alloc memory failed"); return NULL; } } for (i = 0; i < prop_count; i++) { prop_name = (char *)get_field_name_from_meta_index( exec_env, meta_addr, FIELD, i); if (prop_name) { *(prop_name_list + iter_prop_count) = prop_name; iter_prop_count++; } else { wasm_runtime_set_exception( module_inst, "property name get from meta is null"); return NULL; } } /* set prop names into an array */ if (iter_prop_count > 0) { arr = dynamic_new_array(ctx, iter_prop_count); for (i = 0; i < iter_prop_count; i++) { #if WASM_ENABLE_STRINGREF != 0 wasm_stringref_obj_t sringref_obj = create_wasm_string(exec_env, prop_name_list[i]); str = dynamic_new_string( ctx, wasm_stringref_obj_get_value(sringref_obj)); #else str = dynamic_new_string(ctx, prop_name_list[i], strlen(prop_name_list[i])); #endif dynamic_set_elem(ctx, arr, i, str); dyntype_release(ctx, str); } } if (prop_name_list) { wasm_runtime_free(prop_name_list); } } else { wasm_runtime_set_exception(module_inst, "libdyntype: get_keys on non-object"); } return arr; } void extref_unsupported(const char *reason) { wasm_exec_env_t exec_env = dyntype_context_get_exec_env(); wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); wasm_runtime_set_exception(module_inst, reason); } ================================================ FILE: runtime-library/libdyntype/extref/extref.h ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #ifndef __EXTREF_H_ #define __EXTREF_H_ #include "libdyntype.h" int extref_set_elem(dyn_ctx_t ctx, dyn_value_t obj, int index, dyn_value_t elem); dyn_value_t extref_get_elem(dyn_ctx_t ctx, dyn_value_t obj, int index); int extref_set_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop, dyn_value_t value); dyn_value_t extref_get_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop); dyn_value_t extref_get_own_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop); int extref_has_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop); int extref_delete_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop); dyn_value_t extref_invoke(dyn_ctx_t ctx, const char *name, dyn_value_t obj, int argc, dyn_value_t *args); dyn_value_t extref_get_keys(dyn_ctx_t ctx, dyn_value_t obj); void extref_unsupported(const char *reason); #endif /* end of __EXTREF_H_ */ ================================================ FILE: runtime-library/libdyntype/lib_dyntype_wrapper.c ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "gc_export.h" #include "libdyntype_export.h" #include "object_utils.h" #include "type_utils.h" #include "wamr_utils.h" /****************** Context access *****************/ void * dyntype_get_context_wrapper(wasm_exec_env_t exec_env) { dyn_ctx_t ctx = dyntype_get_context(); dyntype_context_set_exec_env(exec_env); return wasm_anyref_obj_new(exec_env, ctx); } /******************* Field access *******************/ wasm_anyref_obj_t dyntype_new_number_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, double value) { RETURN_BOX_ANYREF(dyntype_new_number(UNBOX_ANYREF(ctx), value), UNBOX_ANYREF(ctx)); } wasm_anyref_obj_t dyntype_new_boolean_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, bool value) { RETURN_BOX_ANYREF(dyntype_new_boolean(UNBOX_ANYREF(ctx), value), UNBOX_ANYREF(ctx)); } #if WASM_ENABLE_STRINGREF != 0 wasm_anyref_obj_t dyntype_new_string_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_stringref_obj_t str_obj) { RETURN_BOX_ANYREF(dyntype_new_string(UNBOX_ANYREF(ctx), wasm_stringref_obj_get_value(str_obj)), UNBOX_ANYREF(ctx)); } #else wasm_anyref_obj_t dyntype_new_string_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_struct_obj_t str_obj) { WASMValue arr_obj = { 0 }; uint32_t arr_len = 0; const char *str = ""; wasm_struct_obj_get_field(str_obj, 1, false, &arr_obj); arr_len = wasm_array_obj_length((wasm_array_obj_t)arr_obj.gc_obj); if (arr_len != 0) { str = (char *)wasm_array_obj_first_elem_addr( (wasm_array_obj_t)arr_obj.gc_obj); } RETURN_BOX_ANYREF( dyntype_new_string(UNBOX_ANYREF(ctx), str, arr_len), UNBOX_ANYREF(ctx)); } #endif /* end of WASM_ENABLE_STRINGREF != 0 */ wasm_anyref_obj_t dyntype_new_undefined_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx) { RETURN_BOX_ANYREF(dyntype_new_undefined(UNBOX_ANYREF(ctx)), UNBOX_ANYREF(ctx)); } wasm_anyref_obj_t dyntype_new_null_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx) { RETURN_BOX_ANYREF(dyntype_new_null(UNBOX_ANYREF(ctx)), UNBOX_ANYREF(ctx)); } wasm_anyref_obj_t dyntype_new_object_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx) { RETURN_BOX_ANYREF(dyntype_new_object(UNBOX_ANYREF(ctx)), UNBOX_ANYREF(ctx)); } wasm_anyref_obj_t dyntype_new_array_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, int len) { RETURN_BOX_ANYREF(dyntype_new_array(UNBOX_ANYREF(ctx), len), UNBOX_ANYREF(ctx)); } void dyntype_add_elem_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj, wasm_anyref_obj_t elem) { } wasm_anyref_obj_t dyntype_new_extref_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, void *ptr, external_ref_tag tag) { RETURN_BOX_ANYREF( dyntype_new_extref(UNBOX_ANYREF(ctx), ptr, tag, (void *)exec_env), UNBOX_ANYREF(ctx)); } wasm_anyref_obj_t dyntype_get_keys_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj) { RETURN_BOX_ANYREF(dyntype_get_keys(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj)), UNBOX_ANYREF(ctx)); } void dyntype_set_elem_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj, int index, wasm_anyref_obj_t elem) { dyntype_set_elem(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj), index, UNBOX_ANYREF(elem)); } dyn_value_t dyntype_get_elem_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj, int index) { RETURN_BOX_ANYREF( dyntype_get_elem(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj), index), UNBOX_ANYREF(ctx)); } int dyntype_has_property_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj, const char *prop) { return dyntype_has_property(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj), prop); } int dyntype_delete_property_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj, const char *prop) { return dyntype_delete_property(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj), prop); } int dyntype_set_property_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj, const char *prop, wasm_anyref_obj_t value) { dyn_value_t dyn_ctx = UNBOX_ANYREF(ctx); dyn_value_t dyn_obj = UNBOX_ANYREF(obj); dyn_value_t dyn_value = UNBOX_ANYREF(value); return dyntype_set_property(dyn_ctx, dyn_obj, prop, dyn_value); } dyn_value_t dyntype_get_property_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj, const char *prop) { dyn_value_t dyn_ctx = UNBOX_ANYREF(ctx); dyn_value_t dyn_obj = UNBOX_ANYREF(obj); RETURN_BOX_ANYREF(dyntype_get_property(dyn_ctx, dyn_obj, prop), dyn_ctx); } wasm_anyref_obj_t dyntype_get_own_property_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj, const char *prop) { RETURN_BOX_ANYREF( dyntype_get_own_property(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj), prop), UNBOX_ANYREF(ctx)); } int dyntype_define_property_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj, const char *prop, wasm_anyref_obj_t desc) { return dyntype_define_property(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj), prop, UNBOX_ANYREF(desc)); } /******************* Runtime type checking *******************/ int dyntype_is_undefined_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj) { return dyntype_is_undefined(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj)); } int dyntype_is_null_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj) { return dyntype_is_null(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj)); } int dyntype_is_bool_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj) { return dyntype_is_bool(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj)); } int dyntype_to_bool_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj) { bool value = 0, ret; ret = dyntype_to_bool(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj), &value); if (ret != DYNTYPE_SUCCESS) { wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), "libdyntype: failed to convert to bool"); } return value; } int dyntype_is_number_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj) { return dyntype_is_number(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj)); } double dyntype_to_number_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj) { double value = 0; bool ret; ret = dyntype_to_number(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj), &value); if (ret != DYNTYPE_SUCCESS) { wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), "libdyntype: failed to convert to number"); } return value; } int dyntype_is_string_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj) { return dyntype_is_string(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj)); } #if WASM_ENABLE_STRINGREF != 0 wasm_stringref_obj_t dyntype_to_string_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj) { return wasm_stringref_obj_new( exec_env, dyntype_to_string(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj))); } #else void * dyntype_to_string_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj) { char *value = NULL; int ret; void *new_string_struct = NULL; ret = dyntype_to_cstring(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj), &value); if (ret != DYNTYPE_SUCCESS) { if (value) { dyntype_free_cstring(UNBOX_ANYREF(ctx), value); } wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), "libdyntype: failed to convert to cstring"); return NULL; } new_string_struct = create_wasm_string(exec_env, value); dyntype_free_cstring(UNBOX_ANYREF(ctx), value); return (void *)new_string_struct; } #endif /* end of WASM_ENABLE_STRINGREF != 0 */ int dyntype_is_object_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj) { return dyntype_is_object(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj)); } int dyntype_is_array_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj) { return dyntype_is_array(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj)); } int dyntype_is_extref_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj) { return dyntype_is_extref(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj)); } void * dyntype_to_extref_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj) { void *value = NULL; int ret; ret = dyntype_to_extref(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj), &value); if (ret < ExtObj || ret > ExtArray) { wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), "libdyntype: failed to convert to extref"); } return value; } int dyntype_is_falsy_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t value) { return dyntype_is_falsy(UNBOX_ANYREF(ctx), UNBOX_ANYREF(value)); } void * dyntype_toString_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, #if WASM_ENABLE_STRINGREF != 0 wasm_stringref_obj_t value #else wasm_anyref_obj_t value #endif /* end of WASM_ENABLE_STRINGREF != 0 */ ) { char *str; dyn_type_t type; void *res = NULL; void *table_elem; int32_t table_index; dyn_value_t dyn_ctx, dyn_value; char *tmp_value = NULL; dyn_ctx = UNBOX_ANYREF(ctx); dyn_value = UNBOX_ANYREF(value); if (dyntype_is_extref(dyn_ctx, dyn_value)) { type = dyntype_typeof(dyn_ctx, dyn_value); if (type != DynExtRefArray) { tmp_value = "[object Object]"; if (type == DynExtRefFunc) { tmp_value = "[wasm Function]"; } res = create_wasm_string(exec_env, tmp_value); } else { dyntype_to_extref(dyn_ctx, dyn_value, &table_elem); table_index = (int32_t)(intptr_t)table_elem; table_elem = wamr_utils_get_table_element(exec_env, table_index); res = array_to_string(exec_env, dyn_ctx, table_elem, NULL); } } else { dyntype_to_cstring(dyn_ctx, dyn_value, &str); if (str == NULL) { return NULL; } res = create_wasm_string(exec_env, str); dyntype_free_cstring(dyn_ctx, str); } return res; } /******************* Type equivalence *******************/ /* for typeof keyword*/ void * dyntype_typeof_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj) { dyn_type_t dyn_type; char* value; void *res = NULL; dyn_type = dyntype_typeof(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj)); switch (dyn_type) { case DynUndefined: value = "undefined"; break; case DynBoolean: value = "boolean"; break; case DynNumber: value = "number"; break; case DynString: value = "string"; break; case DynFunction: case DynExtRefFunc: value = "function"; break; case DynNull: case DynObject: case DynExtRefObj: case DynExtRefArray: value = "object"; break; default: wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), "libdyntype: typeof getting unknown type"); value = "unknown"; } res = create_wasm_string(exec_env, value); return res; } /* for internal use, no need to create a wasm string*/ dyn_type_t dyntype_typeof1_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj) { return dyntype_typeof(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj)); } int dyntype_type_eq_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t lhs, wasm_anyref_obj_t rhs) { return dyntype_type_eq(UNBOX_ANYREF(ctx), UNBOX_ANYREF(lhs), UNBOX_ANYREF(rhs)); } int dyntype_cmp_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t lhs, wasm_anyref_obj_t rhs, cmp_operator operator_kind) { int res = 0; dyn_type_t type_l, type_r; bool l_is_null = false, r_is_null = false; void *lhs_ref, *rhs_ref; int32_t lhs_idx, rhs_idx; type_l = dyntype_typeof(UNBOX_ANYREF(ctx), UNBOX_ANYREF(lhs)); type_r = dyntype_typeof(UNBOX_ANYREF(ctx), UNBOX_ANYREF(rhs)); if (type_l == type_r) { res = dyntype_cmp(UNBOX_ANYREF(ctx), UNBOX_ANYREF(lhs), UNBOX_ANYREF(rhs), operator_kind); } if (res) { return res; } if (dyntype_is_null(UNBOX_ANYREF(ctx), UNBOX_ANYREF(lhs))) { l_is_null = true; } if (dyntype_is_null(UNBOX_ANYREF(ctx), UNBOX_ANYREF(rhs))) { r_is_null = true; } // if one of them is undefined, and the other is not undefined if (type_l != type_r && (type_l == DynUndefined || type_r == DynUndefined)) { if (operator_kind == ExclamationEqualsToken || operator_kind == ExclamationEqualsEqualsToken) { res = !res; } return res; } // iff null if ((!l_is_null && (type_l < DynExtRefObj || type_l > DynExtRefArray)) || (!r_is_null && (type_r < DynExtRefObj || type_r > DynExtRefArray))) { if (type_l != type_r && (operator_kind == ExclamationEqualsToken || operator_kind == ExclamationEqualsEqualsToken)) { res = !res; } return res; } if (!l_is_null) { dyntype_to_extref(UNBOX_ANYREF(ctx), UNBOX_ANYREF(lhs), &lhs_ref); lhs_idx = (int32_t)(intptr_t)lhs_ref; lhs_ref = wamr_utils_get_table_element(exec_env, lhs_idx); } else { lhs_ref = NULL; } if (!r_is_null) { dyntype_to_extref(UNBOX_ANYREF(ctx), UNBOX_ANYREF(rhs), &rhs_ref); rhs_idx = (int32_t)(intptr_t)rhs_ref; rhs_ref = wamr_utils_get_table_element(exec_env, rhs_idx); } else { rhs_ref = NULL; } res = lhs_ref == rhs_ref; if (operator_kind == ExclamationEqualsToken || operator_kind == ExclamationEqualsEqualsToken) { res = !res; } return res; } /******************* Subtyping *******************/ wasm_anyref_obj_t dyntype_new_object_with_proto_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, const wasm_anyref_obj_t proto_obj) { RETURN_BOX_ANYREF(dyntype_new_object_with_proto(UNBOX_ANYREF(ctx), UNBOX_ANYREF(proto_obj)), UNBOX_ANYREF(ctx)); } int dyntype_set_prototype_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj, wasm_anyref_obj_t proto_obj) { return dyntype_set_prototype(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj), UNBOX_ANYREF(proto_obj)); } const wasm_anyref_obj_t dyntype_get_prototype_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj) { RETURN_BOX_ANYREF( dyntype_get_prototype(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj)), UNBOX_ANYREF(ctx)); } int dyntype_instanceof_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, const wasm_anyref_obj_t src_obj, const wasm_anyref_obj_t dst_obj) { wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); wasm_module_t module = wasm_runtime_get_module(module_inst); dyn_type_t obj_type; dyn_ctx_t dyn_ctx; dyn_value_t dyn_src; void *table_elem; int32_t table_idx; wasm_obj_t obj; wasm_obj_t inst_obj; wasm_defined_type_t inst_type; dyn_ctx = UNBOX_ANYREF(ctx); dyn_src = UNBOX_ANYREF(src_obj); obj_type = dyntype_typeof(dyn_ctx, dyn_src); // if src is not an extref object, return false if (obj_type < DynExtRefObj) { return 0; } dyntype_to_extref(dyn_ctx, dyn_src, &table_elem); table_idx = (int32_t)(intptr_t)table_elem; table_elem = wamr_utils_get_table_element(exec_env, table_idx); obj = (wasm_obj_t)table_elem; inst_obj = (wasm_obj_t)dst_obj; if (!wasm_obj_is_struct_obj(inst_obj)) { return 0; } inst_type = wasm_obj_get_defined_type(inst_obj); return wasm_obj_is_instance_of_defined_type(obj, inst_type, module); } /******************* Dumping *******************/ void dyntype_dump_value_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj) { return dyntype_dump_value(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj)); } int dyntype_dump_value_buffer_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, wasm_anyref_obj_t obj, void *buffer, int len) { return dyntype_dump_value_buffer(UNBOX_ANYREF(ctx), UNBOX_ANYREF(obj), buffer, len); } wasm_anyref_obj_t dyntype_get_global_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, const char *name) { RETURN_BOX_ANYREF(dyntype_get_global(UNBOX_ANYREF(ctx), name), UNBOX_ANYREF(ctx)); } wasm_anyref_obj_t dyntype_new_object_with_class_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, const char *name, wasm_anyref_obj_t args_array) { dyn_value_t ret = NULL; dyn_value_t dyn_args = UNBOX_ANYREF(args_array); dyn_value_t dyn_ctx = UNBOX_ANYREF(ctx); dyn_value_t *argv = NULL; int argc = 0; int i = 0; argc = dyntype_get_array_length(dyn_ctx, dyn_args); if (argc < 0) { wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), "array length is less than 0"); return NULL; } if (argc) { argv = wasm_runtime_malloc(sizeof(dyn_value_t) * argc); if (!argv) { wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), "alloc memory failed"); return NULL; } } for (i = 0; i < argc; i++) { argv[i] = dyntype_get_elem(dyn_ctx, dyn_args, i); } ret = dyntype_new_object_with_class(dyn_ctx, name, argc, argv); if (!ret) { wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), "dyntype_new_object_with_class failed"); return NULL; } if (argv) { for (i = 0; i < argc; i++) { dyntype_release(dyn_ctx, argv[i]); } wasm_runtime_free(argv); } RETURN_BOX_ANYREF(ret, dyn_ctx); } /******************* Function callback *******************/ wasm_anyref_obj_t dyntype_invoke_wrapper(wasm_exec_env_t exec_env, wasm_anyref_obj_t ctx, const char *name, wasm_anyref_obj_t obj, wasm_anyref_obj_t args_array) { int i = 0; uint32_t argc = 0; dyn_value_t dyn_ctx = UNBOX_ANYREF(ctx); dyn_value_t dyn_obj = UNBOX_ANYREF(obj); dyn_value_t dyn_args = UNBOX_ANYREF(args_array); dyn_value_t *func_args = NULL; dyn_value_t func_ret = NULL; argc = dyntype_get_array_length(dyn_ctx, dyn_args); if (argc < 0) { wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), "array length is less than 0"); return NULL; } if (argc > 0) { func_args = wasm_runtime_malloc(sizeof(dyn_value_t) * argc); if (!func_args) { wasm_runtime_set_exception( wasm_runtime_get_module_inst(exec_env), "alloc memory failed"); return NULL; } } for (i = 0; i < argc; i++) { func_args[i] = dyntype_get_elem(dyn_ctx, dyn_args, i); } func_ret = dyntype_invoke(dyn_ctx, name, dyn_obj, argc, func_args); if (func_args) { for (i = 0; i < argc; i++) { dyntype_release(dyn_ctx, func_args[i]); } wasm_runtime_free(func_args); } RETURN_BOX_ANYREF(func_ret, dyn_ctx); } dyn_value_t dyntype_callback_wasm_dispatcher(void *exec_env_v, dyn_ctx_t ctx, void *vfunc, dyn_value_t this_obj, int argc, dyn_value_t *args) { wasm_exec_env_t exec_env = exec_env_v; uint32_t func_id = (uint32_t)(uintptr_t)vfunc; void *closure = NULL; void *res = NULL; closure = wamr_utils_get_table_element(exec_env, func_id); res = call_wasm_func_with_boxing(exec_env, ctx, (wasm_anyref_obj_t)closure, argc, args); if (!res) { res = dyntype_new_undefined(ctx); } return res; } /* clang-format off */ #define REG_NATIVE_FUNC(func_name, signature) \ { #func_name, func_name##_wrapper, signature, NULL } static NativeSymbol native_symbols[] = { REG_NATIVE_FUNC(dyntype_get_context, "()r"), REG_NATIVE_FUNC(dyntype_new_number, "(rF)r"), REG_NATIVE_FUNC(dyntype_new_boolean, "(ri)r"), REG_NATIVE_FUNC(dyntype_new_string, "(rr)r"), REG_NATIVE_FUNC(dyntype_new_undefined, "(r)r"), REG_NATIVE_FUNC(dyntype_new_null, "(r)r"), REG_NATIVE_FUNC(dyntype_new_object, "(r)r"), REG_NATIVE_FUNC(dyntype_new_array, "(ri)r"), REG_NATIVE_FUNC(dyntype_add_elem, "(rrr)"), REG_NATIVE_FUNC(dyntype_set_elem, "(rrir)"), REG_NATIVE_FUNC(dyntype_get_elem, "(rri)r"), REG_NATIVE_FUNC(dyntype_new_extref, "(rii)r"), REG_NATIVE_FUNC(dyntype_new_object_with_proto, "(rr)r"), REG_NATIVE_FUNC(dyntype_set_prototype, "(rrr)i"), REG_NATIVE_FUNC(dyntype_get_prototype, "(rr)r"), REG_NATIVE_FUNC(dyntype_get_own_property, "(rr$r)r"), REG_NATIVE_FUNC(dyntype_set_property, "(rr$r)i"), REG_NATIVE_FUNC(dyntype_define_property, "(rr$r)i"), REG_NATIVE_FUNC(dyntype_get_property, "(rr$)r"), REG_NATIVE_FUNC(dyntype_has_property, "(rr$)i"), REG_NATIVE_FUNC(dyntype_delete_property, "(rr$)i"), REG_NATIVE_FUNC(dyntype_get_keys, "(rr)r"), REG_NATIVE_FUNC(dyntype_is_undefined, "(rr)i"), REG_NATIVE_FUNC(dyntype_is_null, "(rr)i"), REG_NATIVE_FUNC(dyntype_is_bool, "(rr)i"), REG_NATIVE_FUNC(dyntype_is_number, "(rr)i"), REG_NATIVE_FUNC(dyntype_is_string, "(rr)i"), REG_NATIVE_FUNC(dyntype_is_object, "(rr)i"), REG_NATIVE_FUNC(dyntype_is_array, "(rr)i"), REG_NATIVE_FUNC(dyntype_is_extref, "(rr)i"), REG_NATIVE_FUNC(dyntype_to_bool, "(rr)i"), REG_NATIVE_FUNC(dyntype_to_number, "(rr)F"), REG_NATIVE_FUNC(dyntype_to_string, "(rr)r"), REG_NATIVE_FUNC(dyntype_to_extref, "(rr)i"), REG_NATIVE_FUNC(dyntype_is_falsy, "(rr)i"), REG_NATIVE_FUNC(dyntype_typeof, "(rr)r"), REG_NATIVE_FUNC(dyntype_typeof1, "(rr)i"), REG_NATIVE_FUNC(dyntype_type_eq, "(rrr)i"), REG_NATIVE_FUNC(dyntype_toString, "(rr)r"), REG_NATIVE_FUNC(dyntype_cmp, "(rrri)i"), REG_NATIVE_FUNC(dyntype_instanceof, "(rrr)i"), REG_NATIVE_FUNC(dyntype_new_object_with_class, "(r$r)r"), REG_NATIVE_FUNC(dyntype_invoke, "(r$rr)r"), REG_NATIVE_FUNC(dyntype_get_global, "(r$)r"), /* TODO */ }; /* clang-format on */ uint32_t get_libdyntype_symbols(char **p_module_name, NativeSymbol **p_native_symbols) { *p_module_name = "libdyntype"; *p_native_symbols = native_symbols; return sizeof(native_symbols) / sizeof(NativeSymbol); } ================================================ FILE: runtime-library/libdyntype/libdyntype.c ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "libdyntype.h" #include "libdyntype_export.h" #include "pure_dynamic.h" #include "extref/extref.h" static void *g_exec_env = NULL; static dyntype_callback_dispatcher_t g_cb_dispatcher = NULL; /********************************************/ /* APIs exposed to runtime embedder */ /********************************************/ dyn_ctx_t dyntype_context_init() { return dynamic_context_init(); } dyn_ctx_t dyntype_context_init_with_opt(dyn_options_t *options) { return dynamic_context_init_with_opt(options); } void dyntype_context_destroy(dyn_ctx_t ctx) { g_exec_env = NULL; g_cb_dispatcher = NULL; dynamic_context_destroy(ctx); } void dyntype_context_set_exec_env(void *exec_env) { g_exec_env = exec_env; } void * dyntype_context_get_exec_env() { return g_exec_env; } void dyntype_set_callback_dispatcher(dyntype_callback_dispatcher_t callback) { g_cb_dispatcher = callback; } dyntype_callback_dispatcher_t dyntype_get_callback_dispatcher() { return g_cb_dispatcher; } int dyntype_execute_pending_jobs(dyn_ctx_t ctx) { return dynamic_execute_pending_jobs(ctx); } void dyntype_dump_error(dyn_ctx_t ctx) { dynamic_dump_error(ctx); } dyn_value_t dyntype_throw_exception(dyn_ctx_t ctx, dyn_value_t obj) { return dynamic_throw_exception(ctx, obj); } void dyntype_dump_value(dyn_ctx_t ctx, dyn_value_t obj) { dynamic_dump_value(ctx, obj); } int dyntype_dump_value_buffer(dyn_ctx_t ctx, dyn_value_t obj, void *buffer, int len) { return dynamic_dump_value_buffer(ctx, obj, buffer, len); } dyn_value_t dyntype_hold(dyn_ctx_t ctx, dyn_value_t obj) { return dynamic_hold(ctx, obj); } void dyntype_release(dyn_ctx_t ctx, dyn_value_t obj) { dynamic_release(ctx, obj); } void dyntype_collect(dyn_ctx_t ctx) { dynamic_collect(ctx); } /********************************************/ /* APIs exposed to wasm application */ /********************************************/ #define EXTREF_NOT_ALLOWED(api_name, ret) \ if (dyntype_is_extref(ctx, obj)) { \ extref_unsupported( \ "libdyntype: unsupport operation for extref: " #api_name); \ return ret; \ } #define MIXED_TYPE_DISPATCH(api_name, ...) \ bool is_extref; \ \ is_extref = dyntype_is_extref(ctx, obj); \ if (is_extref) { \ return extref_##api_name(ctx, __VA_ARGS__); \ } \ return dynamic_##api_name(ctx, __VA_ARGS__); dyn_ctx_t dyntype_get_context() { return dynamic_get_context(); } dyn_value_t dyntype_new_number(dyn_ctx_t ctx, double value) { return dynamic_new_number(ctx, value); } dyn_value_t dyntype_new_boolean(dyn_ctx_t ctx, bool value) { return dynamic_new_boolean(ctx, value); } #if WASM_ENABLE_STRINGREF != 0 dyn_value_t dyntype_new_string(dyn_ctx_t ctx, const void *stringref) { return dynamic_new_string(ctx, stringref); } #else dyn_value_t dyntype_new_string(dyn_ctx_t ctx, const char *str, int len) { return dynamic_new_string(ctx, str, len); } #endif dyn_value_t dyntype_new_undefined(dyn_ctx_t ctx) { return dynamic_new_undefined(ctx); } dyn_value_t dyntype_new_null(dyn_ctx_t ctx) { return dynamic_new_null(ctx); } dyn_value_t dyntype_new_object(dyn_ctx_t ctx) { return dynamic_new_object(ctx); } dyn_value_t dyntype_new_object_with_class(dyn_ctx_t ctx, const char *name, int argc, dyn_value_t *args) { return dynamic_new_object_with_class(ctx, name, argc, args); } dyn_value_t dyntype_new_object_with_proto(dyn_ctx_t ctx, const dyn_value_t proto_obj) { return dynamic_new_object_with_proto(ctx, proto_obj); } dyn_value_t dyntype_new_array(dyn_ctx_t ctx, int len) { return dynamic_new_array(ctx, len); } dyn_value_t dyntype_new_extref(dyn_ctx_t ctx, void *ptr, external_ref_tag tag, void *opaque) { return dynamic_new_extref(ctx, ptr, tag, opaque); } int dyntype_set_elem(dyn_ctx_t ctx, dyn_value_t obj, int index, dyn_value_t elem) { MIXED_TYPE_DISPATCH(set_elem, obj, index, elem) } dyn_value_t dyntype_get_elem(dyn_ctx_t ctx, dyn_value_t obj, int index) { MIXED_TYPE_DISPATCH(get_elem, obj, index) } int dyntype_set_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop, dyn_value_t value) { MIXED_TYPE_DISPATCH(set_property, obj, prop, value) } dyn_value_t dyntype_get_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop) { MIXED_TYPE_DISPATCH(get_property, obj, prop) } dyn_value_t dyntype_get_own_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop) { MIXED_TYPE_DISPATCH(get_own_property, obj, prop) } int dyntype_define_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop, dyn_value_t desc) { EXTREF_NOT_ALLOWED(define_property, -DYNTYPE_TYPEERR) return dynamic_define_property(ctx, obj, prop, desc); } int dyntype_has_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop) { MIXED_TYPE_DISPATCH(has_property, obj, prop) } int dyntype_delete_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop) { MIXED_TYPE_DISPATCH(delete_property, obj, prop) } dyn_value_t dyntype_get_keys(dyn_ctx_t ctx, dyn_value_t obj) { bool is_extref; dyn_value_t extref_arr = NULL, dynamic_arr = NULL, total_arr = NULL, tmp_elem = NULL; uint32_t extref_arr_len = 0, dynamic_arr_len = 0, total_arr_len = 0, i = 0; is_extref = dyntype_is_extref(ctx, obj); if (is_extref) { extref_arr = extref_get_keys(ctx, obj); extref_arr_len = dyntype_get_array_length(ctx, extref_arr); } dynamic_arr = dynamic_get_keys(ctx, obj); dynamic_arr_len = dyntype_get_array_length(ctx, dynamic_arr); total_arr_len = extref_arr_len + dynamic_arr_len; total_arr = dyntype_new_array(ctx, total_arr_len); for (i = 0; i < extref_arr_len; i++) { tmp_elem = dyntype_get_elem(ctx, extref_arr, i); dyntype_set_elem(ctx, total_arr, i, tmp_elem); dyntype_release(ctx, tmp_elem); } for (i = 0; i < dynamic_arr_len; i++) { tmp_elem = dyntype_get_elem(ctx, dynamic_arr, i); dyntype_set_elem(ctx, total_arr, i + extref_arr_len, tmp_elem); dyntype_release(ctx, tmp_elem); } if (extref_arr) { dyntype_release(ctx, extref_arr); } if (dynamic_arr) { dyntype_release(ctx, dynamic_arr); } return total_arr; } bool dyntype_is_number(dyn_ctx_t ctx, dyn_value_t obj) { return dynamic_is_number(ctx, obj); } int dyntype_to_number(dyn_ctx_t ctx, dyn_value_t obj, double *pres) { return dynamic_to_number(ctx, obj, pres); } bool dyntype_is_bool(dyn_ctx_t ctx, dyn_value_t obj) { return dynamic_is_bool(ctx, obj); } int dyntype_to_bool(dyn_ctx_t ctx, dyn_value_t bool_obj, bool *pres) { return dynamic_to_bool(ctx, bool_obj, pres); } bool dyntype_is_string(dyn_ctx_t ctx, dyn_value_t obj) { return dynamic_is_string(ctx, obj); } #if WASM_ENABLE_STRINGREF != 0 void * dyntype_to_string(dyn_ctx_t ctx, dyn_value_t obj) { return dynamic_to_string(ctx, obj); } #endif int dyntype_to_cstring(dyn_ctx_t ctx, dyn_value_t str_obj, char **pres) { return dynamic_to_cstring(ctx, str_obj, pres); } void dyntype_free_cstring(dyn_ctx_t ctx, char *str) { dynamic_free_cstring(ctx, str); } bool dyntype_is_undefined(dyn_ctx_t ctx, dyn_value_t obj) { return dynamic_is_undefined(ctx, obj); } bool dyntype_is_null(dyn_ctx_t ctx, dyn_value_t obj) { return dynamic_is_null(ctx, obj); } bool dyntype_is_object(dyn_ctx_t ctx, dyn_value_t obj) { return dynamic_is_object(ctx, obj); } bool dyntype_is_function(dyn_ctx_t ctx, dyn_value_t obj) { return dynamic_is_function(ctx, obj); } bool dyntype_is_array(dyn_ctx_t ctx, dyn_value_t obj) { return dynamic_is_array(ctx, obj); } bool dyntype_is_extref(dyn_ctx_t ctx, dyn_value_t obj) { return dynamic_is_extref(ctx, obj); } int dyntype_to_extref(dyn_ctx_t ctx, dyn_value_t obj, void **pres) { return dynamic_to_extref(ctx, obj, pres); } bool dyntype_is_exception(dyn_ctx_t ctx, dyn_value_t value) { return dynamic_is_exception(ctx, value); } bool dyntype_is_falsy(dyn_ctx_t ctx, dyn_value_t value) { return dynamic_is_falsy(ctx, value); } dyn_type_t dyntype_typeof(dyn_ctx_t ctx, dyn_value_t obj) { return dynamic_typeof(ctx, obj); } bool dyntype_type_eq(dyn_ctx_t ctx, dyn_value_t lhs, dyn_value_t rhs) { return dynamic_type_eq(ctx, lhs, rhs); } bool dyntype_cmp(dyn_ctx_t ctx, dyn_value_t lhs, dyn_value_t rhs, cmp_operator operator_kind) { return dynamic_cmp(ctx, lhs, rhs, operator_kind); } int dyntype_set_prototype(dyn_ctx_t ctx, dyn_value_t obj, const dyn_value_t proto_obj) { EXTREF_NOT_ALLOWED(set_prototype, -DYNTYPE_TYPEERR) return dynamic_set_prototype(ctx, obj, proto_obj); } dyn_value_t dyntype_get_prototype(dyn_ctx_t ctx, dyn_value_t obj) { EXTREF_NOT_ALLOWED(get_prototype, NULL) return dynamic_get_prototype(ctx, obj); } bool dyntype_instanceof(dyn_ctx_t ctx, const dyn_value_t src_obj, const dyn_value_t dst_obj) { return dynamic_instanceof(ctx, src_obj, dst_obj); } dyn_value_t dyntype_invoke(dyn_ctx_t ctx, const char *name, dyn_value_t obj, int argc, dyn_value_t *args) { MIXED_TYPE_DISPATCH(invoke, name, obj, argc, args) } dyn_value_t dyntype_get_global(dyn_ctx_t ctx, const char *name) { return dynamic_get_global(ctx, name); } int dyntype_get_array_length(dyn_ctx_t ctx, dyn_value_t obj) { return dynamic_get_array_length(ctx, obj); } ================================================ FILE: runtime-library/libdyntype/libdyntype.cmake ================================================ # # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # set (LIBDYNTYPE_DIR ${CMAKE_CURRENT_LIST_DIR}) include_directories (${LIBDYNTYPE_DIR}) if (NOT USE_SIMPLE_LIBDYNTYPE EQUAL 1) message(" * Use libdyntype implemented based on quickjs") include_directories(${LIBDYNTYPE_DIR}/dynamic-qjs) file (GLOB dynamic_impl_src ${LIBDYNTYPE_DIR}/dynamic-qjs/*.c ) else() message(" * Use simple libdyntype implementation") include_directories(${LIBDYNTYPE_DIR}/dynamic-simple) include_directories(${LIBDYNTYPE_DIR}/dynamic-simple/dyn-value) file (GLOB_RECURSE dynamic_impl_src ${LIBDYNTYPE_DIR}/dynamic-simple/*.c ) endif() file (GLOB source_all ${LIBDYNTYPE_DIR}/*.c ${LIBDYNTYPE_DIR}/extref/*.c ${dynamic_impl_src} ) set (LIBDYNTYPE_SRC ${source_all}) ================================================ FILE: runtime-library/libdyntype/libdyntype.h ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #ifndef __LIBDYNTYPE_H_ #define __LIBDYNTYPE_H_ #include #include #include #ifdef __cplusplus extern "C" { #endif #define DYNTYPE_FALSE 0 #define DYNTYPE_TRUE 1 #define DYNTYPE_SUCCESS 0 #define DYNTYPE_EXCEPTION 1 #define DYNTYPE_TYPEERR 2 struct DynTypeContext; typedef struct DynTypeContext *dyn_ctx_t; typedef void dyn_options_t; typedef void *dyn_value_t; typedef dyn_value_t (*dyntype_callback_dispatcher_t)(void *env, dyn_ctx_t ctx, void *vfunc, dyn_value_t this_obj, int argc, dyn_value_t *args); typedef enum external_ref_tag { ExtObj, ExtFunc, ExtArray, } external_ref_tag; typedef enum dyn_type_t { DynUnknown, DynNull, DynUndefined, DynObject, DynBoolean, DynNumber, DynString, DynFunction, DynSymbol, DynBigInt, DynExtRefObj, DynExtRefFunc, DynExtRefArray, } dyn_type_t; typedef enum cmp_operator { LessThanToken = 29, GreaterThanToken = 31, LessThanEqualsToken = 32, GreaterThanEqualsToken = 33, EqualsEqualsToken = 34, ExclamationEqualsToken = 35, EqualsEqualsEqualsToken = 36, ExclamationEqualsEqualsToken = 37, } cmp_operator; /***************************************************************** * * * Section 2 * * * * Interface exposed to application * * * *****************************************************************/ /****************** Context access *****************/ /** * @brief Get the global dynamic type system context * * @return dynamic type system context if success, NULL otherwise */ dyn_ctx_t dyntype_get_context(); /******************* Field access *******************/ /* Creation */ /** * @brief Boxing a number to dynamic value * * @param ctx the dynamic type system context * @param value the number to be boxed * @return dynamic value if success, NULL otherwise */ dyn_value_t dyntype_new_number(dyn_ctx_t ctx, double value); /** * @brief Boxing a bool to dynamic value * * @param ctx the dynamic type system context * @param value the bool value to be boxed * @return dynamic value if success, NULL otherwise */ dyn_value_t dyntype_new_boolean(dyn_ctx_t ctx, bool value); #if WASM_ENABLE_STRINGREF != 0 dyn_value_t dyntype_new_string(dyn_ctx_t ctx, const void *stringref); #else /** * @brief Create a new dynamic string value with the given char* and len * * @param ctx the dynamic type system context * @param str the string to initialize the dynamic value * @param len the length of the given string * @return dynamic value if success, NULL otherwise */ dyn_value_t dyntype_new_string(dyn_ctx_t ctx, const char *str, int len); #endif /* end of WASM_ENABLE_STRINGREF != 0 */ /** * @brief Create a undefined value * * @param ctx the dynamic type system context * @return dynamic undefined value if success, NULL otherwise */ dyn_value_t dyntype_new_undefined(dyn_ctx_t ctx); /** * @brief Create a null value * * @param ctx the dynamic type system context * @return dynamic null value if success, NULL otherwise */ dyn_value_t dyntype_new_null(dyn_ctx_t ctx); /** * @brief Create a new dynamic object without any property * * @param ctx the dynamic type system context * @return dynamic value if success, NULL otherwise */ dyn_value_t dyntype_new_object(dyn_ctx_t ctx); /** * @brief Create new object with given prototype * * @param ctx the dynamic type system context * @param proto_obj prototype object * @return dynamic value if success, NULL otherwise */ dyn_value_t dyntype_new_object_with_proto(dyn_ctx_t ctx, const dyn_value_t proto_obj); /** * @brief Create an object with class name * * @param ctx the dynamic type system context * @param name the name of class * @param argc the count of arguments * @param args the argument array * @return dynamic value if success, NULL otherwise */ dyn_value_t dyntype_new_object_with_class(dyn_ctx_t ctx, const char *name, int argc, dyn_value_t *args); /** * @brief Create a new dynamic array object with array length * * @param ctx the dynamic type system context * @param len array length * @return dynamic value if success, NULL otherwise */ dyn_value_t dyntype_new_array(dyn_ctx_t ctx, int len); /** * @brief Boxing an external reference to a dynamic value * * @param ctx the dynamic type system context * @param ptr opaque pointer to external reference * @param tag external reference tag * @return dynamic value if success, NULL otherwise */ dyn_value_t dyntype_new_extref(dyn_ctx_t ctx, void *ptr, external_ref_tag tag, void* opaque); /** * @brief Set the value element of a dynamic object by index. * * @param ctx the dynamic type system context * @param obj dynamic object * @param index the index of the element to be set * @param elem the value to be set to the element * @return 0: SUCCESS, -1: EXCEPTION, -2: TYPE ERROR */ int dyntype_set_elem(dyn_ctx_t ctx, dyn_value_t obj, int index, dyn_value_t elem); /** * @brief Get the value of a dynamic object by index. * * @param ctx the dynamic type system context * @param obj dynamic object * @param index the index of the element to be get * @return dynamic value if success, NULL otherwise */ dyn_value_t dyntype_get_elem(dyn_ctx_t ctx, dyn_value_t obj, int index); /** * @brief Set the property of a dynamic object * * @param ctx the dynamic type system context * @param obj dynamic object * @param prop property name * @param value the value to be set to the property * @return 0 if success, error code otherwise * @retval -1:EXCEPTION, -2: TYPE ERROR */ int dyntype_set_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop, dyn_value_t value); /** * @brief Get the property of a dynamic object * * @param ctx the dynamic type system context * @param obj dynamic object * @param prop property name * @return dynamic value if success, NULL otherwise */ dyn_value_t dyntype_get_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop); /** * @brief Get own property of the given dynamic object * * @param ctx the dynamic type system context * @param obj dynamic object * @param prop property name * @return dynamic value of the corresponding property if exists, NULL otherwise */ dyn_value_t dyntype_get_own_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop); /** * @brief Define the property of a dynamic object * * @param ctx the dynamic type system context * @param obj dynamic object * @param prop property name * @param value the value to be set to the property * @return 0 if success, error code otherwise * @retval -1: EXCEPTION, -2: TYPE ERROR */ int dyntype_define_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop, dyn_value_t desc); /** * @brief Test if the property exists on the given object * * @param ctx the dynamic type system context * @param obj dynamic object * @param prop property name * @return TRUE if exists, FALSE if not exists, -1 if EXCEPTION */ int dyntype_has_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop); /** * @brief Delete the property of the given object * * @param ctx the dynamic type system context * @param obj dynamic object * @param prop property name * @return TRUE if success, FALSE if failed, -1 if EXCEPTION */ int dyntype_delete_property(dyn_ctx_t ctx, dyn_value_t obj, const char *prop); /** * @brief Get the enumerable properties of the given object * * @param ctx the dynamic type system context * @param obj dynamic object * @return dynamic array which store all property names */ dyn_value_t dyntype_get_keys(dyn_ctx_t ctx, dyn_value_t obj); /******************* Runtime type checking *******************/ /* number */ bool dyntype_is_number(dyn_ctx_t ctx, dyn_value_t obj); int dyntype_to_number(dyn_ctx_t ctx, dyn_value_t obj, double *pres); /* boolean */ bool dyntype_is_bool(dyn_ctx_t ctx, dyn_value_t obj); int dyntype_to_bool(dyn_ctx_t ctx, dyn_value_t bool_obj, bool *pres); /* string */ bool dyntype_is_string(dyn_ctx_t ctx, dyn_value_t obj); #if WASM_ENABLE_STRINGREF != 0 void * dyntype_to_string(dyn_ctx_t ctx, dyn_value_t obj); #endif int dyntype_to_cstring(dyn_ctx_t ctx, dyn_value_t str_obj, char **pres); void dyntype_free_cstring(dyn_ctx_t ctx, char *str); /* undefined and null */ bool dyntype_is_undefined(dyn_ctx_t ctx, dyn_value_t obj); bool dyntype_is_null(dyn_ctx_t ctx, dyn_value_t obj); /* object */ bool dyntype_is_object(dyn_ctx_t ctx, dyn_value_t obj); /* function */ bool dyntype_is_function(dyn_ctx_t ctx, dyn_value_t obj); /* array */ bool dyntype_is_array(dyn_ctx_t ctx, dyn_value_t obj); /* extern ref */ bool dyntype_is_extref(dyn_ctx_t ctx, dyn_value_t obj); /** * @brief Get the extern reference pointer * * @param ctx the dynamic type system context * @param obj dynamic object * @param pres [OUTPUT] pointer to the result * @return external_ref_tag if success, negative error code otherwise */ int dyntype_to_extref(dyn_ctx_t ctx, dyn_value_t obj, void **pres); /** * @brief Check if a dynamic value is an exception * * @param ctx the dynamic type system context * @param value dynamic object * @return TRUE if the value is exception, FALSE otherwise */ bool dyntype_is_exception(dyn_ctx_t ctx, dyn_value_t value); /** * @brief Get the value of any type as a bool condition * * @param ctx the dynamic type system context * @param value dynamic object * @return TRUE if the value is falsy, FALSE otherwise */ bool dyntype_is_falsy(dyn_ctx_t ctx, dyn_value_t value); /******************* Type equivalence *******************/ /** * @brief Get actual type of the dynamic value * * @param ctx the dynamic type system context * @param obj dynamic object * @return type of the dynamic value */ dyn_type_t dyntype_typeof(dyn_ctx_t ctx, dyn_value_t obj); /** * @brief Check if two dynamic value has the same type * * @param ctx the dynamic type system context * @param lhs left hand operand * @param rhs right hand operand * @return true if the two dynamic values have same type (shape), false * otherwise */ bool dyntype_type_eq(dyn_ctx_t ctx, dyn_value_t lhs, dyn_value_t rhs); /** * @brief Compare two dynamic values * * @param ctx the dynamic type system context * @param lhs left hand operand * @param rhs right hand operand * @param operator_kind the compare operator_kind * @return true if the two dynamic values compares are equal, false * otherwise */ bool dyntype_cmp(dyn_ctx_t ctx, dyn_value_t lhs, dyn_value_t rhs, cmp_operator operator_kind); /******************* Subtyping *******************/ /** * @brief Set prototype of the given dynamic object * * @param ctx the dynamic type system context * @param obj dynamic object * @param proto_obj the prototype object * @return 0 if success, error code otherwise * @retval -1:EXCEPTION, -2: TYPE ERROR */ int dyntype_set_prototype(dyn_ctx_t ctx, dyn_value_t obj, const dyn_value_t proto_obj); /** * @brief Get the prototype of the given dynamic object * * @param ctx the dynamic type system context * @param obj dynamic object * @return prototype object, NULL if failed */ dyn_value_t dyntype_get_prototype(dyn_ctx_t ctx, dyn_value_t obj); /******************* property access *******************/ /** * @brief Check if the src object is instance of the dst object * * @param ctx the dynamic type system context * @param src_obj src object * @param dst_obj dst object * @return true if src object is instance of dst object, false otherwise */ bool dyntype_instanceof(dyn_ctx_t ctx, const dyn_value_t src_obj, const dyn_value_t dst_obj); /******************* function fallback *******************/ /** * @brief invoke a dynamic typed function * * @param name the method name if invoking a method, NULL otherwise * @param obj if name is not NULL, obj is the this object, otherwise obj is the function object * @param argc the count of arguments * @param args the argument array * @return dynamic value returned by the function */ dyn_value_t dyntype_invoke(dyn_ctx_t ctx, const char *name, dyn_value_t obj, int argc, dyn_value_t *args); /** * @brief get builtin global object by name * * @param name the name of object * @return dynamic value if success, NULL otherwise */ dyn_value_t dyntype_get_global(dyn_ctx_t ctx, const char *name); #ifdef __cplusplus } #endif #endif /* end of __LIBDYNTYPE_H_ */ ================================================ FILE: runtime-library/libdyntype/libdyntype_export.h ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #ifndef __LIBDYNTYPE_EXPORT_H_ #define __LIBDYNTYPE_EXPORT_H_ #include "libdyntype.h" #ifdef __cplusplus extern "C" { #endif /***************************************************************** * * * Section 1 * * * * Interface exposed to the runtime embedder * * * *****************************************************************/ /******************* Initialization and destroy *****************/ /** * @brief Initialize the dynamic type system context * * @return dynamic type system context if success, NULL otherwise */ dyn_ctx_t dyntype_context_init(); /** * @brief Initialize the dynamic type system context with given options * * @note options can contain allocator functions and maybe other GC related * things * * @param options options to set (TBD) * @return dynamic type system context if success, NULL otherwise */ dyn_ctx_t dyntype_context_init_with_opt(dyn_options_t *options); /** * @brief Destroy the dynamic type system context * * @param ctx context to destroy */ void dyntype_context_destroy(dyn_ctx_t ctx); /** * @brief Bind an execution environment to libdyntype * * @param exec_env the execution environment to bind */ void dyntype_context_set_exec_env(void *exec_env); /** * @brief Get the execution environment bound to libdyntype * * @return the execution environment bound to libdyntype */ void * dyntype_context_get_exec_env(); /** * @brief Set the callback dispatcher for external functions. When calling * dyntype_invoke API, the argument may contain external functions which may be * invoked later (e.g. Map.forEach). Libdyntype doesn't know how to invoke the * external functions since they are not raw native pointers. The callback * dispatcher will be used as a common wrapper for calling all external * functions from libdyntype, so the implementer can decide how to invoke the * actual function. * * @note If another callback is set, the previous one will be overwrite. * * @param callback the callback to set */ void dyntype_set_callback_dispatcher(dyntype_callback_dispatcher_t callback); /** * @brief Get the callback dispatcher for external functions. * * @return the callback dispatcher for external functions */ dyntype_callback_dispatcher_t dyntype_get_callback_dispatcher(); /******************* event loop *******************/ /** * @brief execute pending jobs in micro-tasks of js runtime * @param ctx the dynamic type system context */ int dyntype_execute_pending_jobs(dyn_ctx_t ctx); /******************* Dumping *******************/ /** * @brief Dump dynamic error to stdout * * @param ctx the dynamic type system context */ void dyntype_dump_error(dyn_ctx_t ctx); /******************* Exception *******************/ /** * @brief Throw dynamic exception * * @param ctx the dynamic type system context * @param obj the dynamic exception value */ dyn_value_t dyntype_throw_exception(dyn_ctx_t ctx, dyn_value_t obj); /******************* Dumping *******************/ /** * @brief Dump dynamic value to stdout * * @param ctx the dynamic type system context * @param obj object to be dumped */ void dyntype_dump_value(dyn_ctx_t ctx, dyn_value_t obj); /** * @brief Dump dynamic value to given buffer * * @param ctx the dynamic type system context * @param obj object to be dumped * @param buffer buffer to store the dumped message * @param len length of the given buffer * @return On success, this function return length of bytes dumped to buffer. * When failed, a negative error code is returned and content in buffer is * undefined */ int dyntype_dump_value_buffer(dyn_ctx_t ctx, dyn_value_t obj, void *buffer, int len); /******************* Garbage collection *******************/ /** * @brief Mark the object * * @param ctx the dynamic type system context * @param obj the dynamic value * @return On success, this function return a dyn_value_t which hold a strong * reference to the dynamic object, avoid this object to be claimed until this * dyn_value_t is freed through dyntype_release. */ dyn_value_t dyntype_hold(dyn_ctx_t ctx, dyn_value_t obj); /** * @brief Release the object * * @param ctx the dynamic type system context * @param obj the dynamic value */ void dyntype_release(dyn_ctx_t ctx, dyn_value_t obj); /** * @brief Start GC collect * * @param ctx the dynamic type system context */ void dyntype_collect(dyn_ctx_t ctx); /** * @brief Get array's length * * @param ctx the dynamic type system context * @param obj the dynamic value * @return On success, this function return the array's length. */ int dyntype_get_array_length(dyn_ctx_t ctx, dyn_value_t obj); #ifdef __cplusplus } #endif #endif /* end of __LIBDYNTYPE_EXPORT_H_ */ ================================================ FILE: runtime-library/libdyntype/test/CMakeLists.txt ================================================ # # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # cmake_minimum_required (VERSION 2.8) project(unittest) set (CMAKE_CXX_STANDARD 14) set (UNITTEST_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}) set (LIBDYNTYPE_ROOT_DIR ${UNITTEST_ROOT_DIR}/..) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") if (EXISTS /usr/src/gtest) # Use googletest installed in system add_subdirectory(/usr/src/gtest ${CMAKE_CURRENT_BINARY_DIR}/gtest) else () include(FetchContent) FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG release-1.12.1 ) message("Fetching googletest ...") FetchContent_MakeAvailable(googletest) endif() include(GoogleTest) enable_testing() # add lcov support commands set(CMAKE_C_FLAGS "-fprofile-arcs -ftest-coverage ${CMAKE_C_FLAGS}") set(CMAKE_CXX_FLAGS "-fprofile-arcs -ftest-coverage ${CMAKE_CXX_FLAGS}") add_custom_command(OUTPUT cov-display COMMAND lcov -d . -c -o "test.info" COMMAND lcov --remove test.info '/usr/*' -o test.info COMMAND lcov --remove test.info '*/test/*' -o test.info COMMAND lcov --remove test.info '*/quickjs/*' -o test.info COMMAND genhtml test.info -o result COMMENT "generate html" ) add_custom_target(cov-test DEPENDS cov-display) if (UNITTEST_USE_SANITIZER EQUAL 1) message("Sanitizer enabled") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=signed-integer-overflow \ -fsanitize=undefined -fsanitize=address \ -fno-sanitize=bounds,bounds-strict,alignment \ -fno-sanitize-recover") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=signed-integer-overflow \ -fsanitize=undefined -fsanitize=address \ -fno-sanitize=bounds,bounds-strict,alignment \ -fno-sanitize-recover") endif () include(${LIBDYNTYPE_ROOT_DIR}/../wamr_config.cmake) include_directories(${LIBDYNTYPE_ROOT_DIR}/../deps/quickjs) add_subdirectory(${LIBDYNTYPE_ROOT_DIR} ${CMAKE_CURRENT_BINARY_DIR}/libdyntype) include_directories(${LIBDYNTYPE_ROOT_DIR}) add_executable( dyntype_test ${WAMR_STRINGREF_IMPL_SOURCE} ${CMAKE_CURRENT_LIST_DIR}/types_test.cc ${CMAKE_CURRENT_LIST_DIR}/object_property_test.cc ${CMAKE_CURRENT_LIST_DIR}/operator_test.cc ${CMAKE_CURRENT_LIST_DIR}/prototype_test.cc ${CMAKE_CURRENT_LIST_DIR}/dump.cc ) target_link_libraries(dyntype_test dyntype gtest_main gcov) gtest_discover_tests(dyntype_test) ================================================ FILE: runtime-library/libdyntype/test/dump.cc ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "libdyntype_export.h" #include "stringref/string_object.h" #include class DumpValueTest : public testing::Test { protected: virtual void SetUp() { ctx = dyntype_context_init(); if (ctx == NULL) { } } virtual void TearDown() { dyntype_context_destroy(ctx); } dyn_ctx_t ctx; }; TEST_F(DumpValueTest, dump_value) { char const *str_values[] = { "2147483649.1", "false", "1\"123456\"", "123456" }; char *buffer = new char[10 * 1024]; // number testing::internal::CaptureStdout(); double value = 2147483649.1; dyn_value_t num = dyntype_new_number(ctx, value); dyntype_dump_value(ctx, num); const std::string output1 = testing::internal::GetCapturedStdout(); EXPECT_STREQ(output1.c_str(), str_values[0]); dyntype_dump_value_buffer(ctx, num, buffer, 10 * 1024); EXPECT_STREQ(buffer, str_values[0]); dyntype_release(ctx, num); // boolean testing::internal::CaptureStdout(); dyn_value_t boolean = dyntype_new_boolean(ctx, false); dyntype_dump_value(ctx, boolean); const std::string output2 = testing::internal::GetCapturedStdout(); EXPECT_STREQ(output2.c_str(), str_values[1]); dyntype_dump_value_buffer(ctx, boolean, buffer, 10 * 1024); EXPECT_STREQ(buffer, str_values[1]); dyntype_release(ctx, boolean); // string testing::internal::CaptureStdout(); // the output contains refer_count, output like "1`123456" #if WASM_ENABLE_STRINGREF != 0 WASMString wasm_string = wasm_string_new_const("123456", strlen("123456")); dyn_value_t str = dyntype_new_string(ctx, wasm_string); wasm_string_destroy(wasm_string); #else dyn_value_t str = dyntype_new_string(ctx, "123456", strlen("123456")); #endif dyntype_dump_value(ctx, str); const std::string output3 = testing::internal::GetCapturedStdout(); EXPECT_STREQ(output3.c_str(), str_values[3]); dyntype_dump_value_buffer(ctx, str, buffer, 10 * 1024); EXPECT_STREQ(buffer, str_values[2]); dyntype_release(ctx, str); delete[] buffer; } ================================================ FILE: runtime-library/libdyntype/test/object_property_test.cc ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "libdyntype_export.h" #include #include #include "stringref/string_object.h" #include "test_app.h" #include "wasm_export.h" class ObjectPropertyTest : public testing::Test { protected: virtual void SetUp() { ctx = dyntype_context_init(); } virtual void TearDown() { dyntype_context_destroy(ctx); } dyn_ctx_t ctx; }; TEST_F(ObjectPropertyTest, object_set_and_has_and_get_property) { dyn_value_t obj = dyntype_new_object(ctx); int ext_data = 1000; dyn_value_t num = dyntype_new_number(ctx, 2147483649); dyn_value_t boolean = dyntype_new_boolean(ctx, true); dyn_value_t undefined = dyntype_new_undefined(ctx); dyn_value_t null = dyntype_new_null(ctx); #if WASM_ENABLE_STRINGREF != 0 WASMString wasm_string = wasm_string_new_const("string", strlen("string")); dyn_value_t str = dyntype_new_string(ctx, wasm_string); #else dyn_value_t str = dyntype_new_string(ctx, "string", strlen("string")); #endif dyn_value_t array = dyntype_new_array(ctx, 0); dyn_value_t extref = dyntype_new_extref(ctx, (void *)(uintptr_t)ext_data, external_ref_tag::ExtObj, NULL); dyn_value_t obj1 = dyntype_new_object(ctx); EXPECT_EQ(dyntype_set_property(ctx, obj, "prop1", num), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_set_property(ctx, obj, "prop2", boolean), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_set_property(ctx, obj, "prop3", undefined), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_set_property(ctx, obj, "prop4", null), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_set_property(ctx, obj, "prop5", str), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_set_property(ctx, obj, "prop6", array), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_set_property(ctx, obj, "prop7", extref), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_set_property(ctx, obj, "prop8", obj1), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop1"), 1); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop2"), 1); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop3"), 1); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop4"), 1); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop5"), 1); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop6"), 1); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop7"), 1); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop8"), 1); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop9"), 0); dyn_value_t num_v = dyntype_get_property(ctx, obj, "prop1"); double v = 0; dyntype_to_number(ctx, num_v, &v); EXPECT_EQ(v, 2147483649); dyntype_release(ctx, num_v); dyn_value_t boolean_v = dyntype_get_property(ctx, obj, "prop2"); bool v1 = false; dyntype_to_bool(ctx, boolean_v, &v1); EXPECT_EQ(v1, true); dyntype_release(ctx, boolean_v); dyn_value_t undefined_v = dyntype_get_property(ctx, obj, "prop3"); EXPECT_TRUE(dyntype_is_undefined(ctx, undefined_v)); dyntype_release(ctx, undefined_v); dyn_value_t null_v = dyntype_get_property(ctx, obj, "prop4"); EXPECT_TRUE(dyntype_is_null(ctx, null_v)); dyntype_release(ctx, null_v); dyn_value_t str_v = dyntype_get_property(ctx, obj, "prop5"); char const *target = "string"; char *v2 = nullptr; dyntype_to_cstring(ctx, str_v, &v2); EXPECT_STREQ(v2, target); dyntype_free_cstring(ctx, v2); dyntype_release(ctx, str_v); dyn_value_t array_v = dyntype_get_property(ctx, obj, "prop6"); EXPECT_TRUE(dyntype_is_array(ctx, array_v)); dyntype_release(ctx, array_v); dyn_value_t extref_v = dyntype_get_property(ctx, obj, "prop7"); EXPECT_TRUE(dyntype_is_extref(ctx, extref_v)); dyntype_release(ctx, extref_v); dyn_value_t obj1_v = dyntype_get_property(ctx, obj, "prop8"); EXPECT_TRUE(dyntype_is_object(ctx, obj1_v)); dyntype_release(ctx, obj1_v); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop1"), 1); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop2"), 1); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop3"), 1); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop4"), 1); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop5"), 1); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop6"), 1); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop7"), 1); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop8"), 1); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop9"), 0); dyntype_dump_value(ctx, obj); char *buffer = new char[1024 * 10]; dyntype_dump_value_buffer(ctx, obj, buffer, 1024 * 10); delete[] buffer; void *extref_prop = nullptr; EXPECT_EQ(dyntype_to_extref(ctx, extref, &extref_prop), ExtObj); EXPECT_EQ((int)(uintptr_t)extref_prop, 1000); EXPECT_EQ(dyntype_delete_property(ctx, obj, "prop7"), 1); dyntype_release(ctx, extref); dyntype_release(ctx, obj); dyntype_release(ctx, undefined); dyntype_release(ctx, null); dyntype_release(ctx, num); dyntype_release(ctx, boolean); dyntype_release(ctx, str); dyntype_release(ctx, array); dyntype_release(ctx, obj1); #if WASM_ENABLE_STRINGREF != 0 wasm_string_destroy(wasm_string); #endif } TEST_F(ObjectPropertyTest, object_define_and_has_and_get_property) { dyn_value_t obj = dyntype_new_object(ctx); int ext_data = 1000; dyn_value_t num = dyntype_new_number(ctx, -10.1); dyn_value_t boolean = dyntype_new_boolean(ctx, true); dyn_value_t undefined = dyntype_new_undefined(ctx); dyn_value_t null = dyntype_new_null(ctx); #if WASM_ENABLE_STRINGREF != 0 WASMString wasm_string = wasm_string_new_const(" ", 2); dyn_value_t str = dyntype_new_string(ctx, wasm_string); #else dyn_value_t str = dyntype_new_string(ctx, " ", 2); #endif dyn_value_t array = dyntype_new_array(ctx, 0); dyn_value_t extref = dyntype_new_extref(ctx, (void *)(uintptr_t)ext_data, external_ref_tag::ExtObj, NULL); dyn_value_t obj1 = dyntype_new_object(ctx); dyn_value_t desc1 = dyntype_new_object(ctx); dyn_value_t desc1_v = dyntype_new_boolean(ctx, true); dyntype_set_property(ctx, desc1, "configurable", desc1_v); dyntype_set_property(ctx, desc1, "value", num); dyntype_release(ctx, desc1_v); dyn_value_t desc2 = dyntype_new_object(ctx); dyn_value_t desc2_v = dyntype_new_boolean(ctx, true); dyntype_set_property(ctx, desc2, "writable", desc2_v); dyntype_set_property(ctx, desc2, "value", boolean); dyntype_release(ctx, desc2_v); dyn_value_t desc3 = dyntype_new_object(ctx); dyn_value_t desc3_v = dyntype_new_boolean(ctx, true); dyntype_set_property(ctx, desc3, "enumerable", desc3_v); dyntype_set_property(ctx, desc3, "value", undefined); dyntype_release(ctx, desc3_v); dyn_value_t desc4 = dyntype_new_object(ctx); dyn_value_t desc4_v = dyntype_new_boolean(ctx, false); dyntype_set_property(ctx, desc4, "configurable", desc4_v); dyntype_set_property(ctx, desc4, "value", null); dyntype_release(ctx, desc4_v); dyn_value_t desc5 = dyntype_new_object(ctx); dyn_value_t desc5_v = dyntype_new_boolean(ctx, false); dyntype_set_property(ctx, desc5, "writable", desc5_v); dyntype_set_property(ctx, desc5, "value", str); dyntype_release(ctx, desc5_v); dyn_value_t desc6 = dyntype_new_object(ctx); dyn_value_t desc6_v = dyntype_new_boolean(ctx, false); dyntype_set_property(ctx, desc6, "enumerable", desc6_v); dyntype_set_property(ctx, desc6, "value", array); dyntype_release(ctx, desc6_v); dyn_value_t desc7 = dyntype_new_object(ctx); dyn_value_t desc7_v = dyntype_new_boolean(ctx, true); dyntype_set_property(ctx, desc7, "configurable", desc7_v); dyntype_set_property(ctx, desc7, "value", extref); dyntype_release(ctx, desc7_v); dyn_value_t desc8 = dyntype_new_object(ctx); dyn_value_t desc8_v = dyntype_new_boolean(ctx, true); dyntype_set_property(ctx, desc8, "writable", desc8_v); dyntype_set_property(ctx, desc8, "value", obj1); dyntype_release(ctx, desc8_v); EXPECT_EQ(dyntype_define_property(ctx, obj, "prop1", desc1), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_define_property(ctx, obj, "prop2", desc2), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_define_property(ctx, obj, "prop3", desc3), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_define_property(ctx, obj, "prop4", desc4), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_define_property(ctx, obj, "prop5", desc5), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_define_property(ctx, obj, "prop6", desc6), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_define_property(ctx, obj, "prop7", desc7), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_define_property(ctx, obj, "prop8", desc8), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop1"), 1); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop2"), 1); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop3"), 1); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop4"), 1); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop5"), 1); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop6"), 1); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop7"), 1); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop8"), 1); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop9"), 0); dyn_value_t bool_obj = dyntype_new_boolean(ctx, false); EXPECT_EQ(dyntype_define_property(ctx, obj, "prop not a object", bool_obj), -DYNTYPE_TYPEERR); dyntype_release(ctx, bool_obj); dyn_value_t num_v = dyntype_get_property(ctx, obj, "prop1"); double v = 0; dyntype_to_number(ctx, num_v, &v); EXPECT_EQ(v, -10.1); dyntype_release(ctx, num_v); dyn_value_t boolean_v = dyntype_get_property(ctx, obj, "prop2"); bool v1 = false; dyntype_to_bool(ctx, boolean_v, &v1); EXPECT_EQ(v1, true); dyntype_release(ctx, boolean_v); dyn_value_t undefined_v = dyntype_get_property(ctx, obj, "prop3"); EXPECT_TRUE(dyntype_is_undefined(ctx, undefined_v)); dyntype_release(ctx, undefined_v); dyn_value_t null_v = dyntype_get_property(ctx, obj, "prop4"); EXPECT_TRUE(dyntype_is_null(ctx, null_v)); dyntype_release(ctx, null_v); dyn_value_t str_v = dyntype_get_property(ctx, obj, "prop5"); char const *target = " "; char *v2 = nullptr; dyntype_to_cstring(ctx, str_v, &v2); EXPECT_STREQ(v2, target); dyntype_free_cstring(ctx, v2); dyntype_release(ctx, str_v); dyn_value_t array_v = dyntype_get_property(ctx, obj, "prop6"); EXPECT_TRUE(dyntype_is_array(ctx, array_v)); dyntype_release(ctx, array_v); dyn_value_t extref_v = dyntype_get_property(ctx, obj, "prop7"); EXPECT_TRUE(dyntype_is_extref(ctx, extref_v)); dyntype_release(ctx, extref_v); dyn_value_t obj1_v = dyntype_get_property(ctx, obj, "prop8"); EXPECT_TRUE(dyntype_is_object(ctx, obj1_v)); dyntype_release(ctx, obj1_v); void *extref_prop = nullptr; EXPECT_EQ(dyntype_to_extref(ctx, extref, &extref_prop), DYNTYPE_SUCCESS); EXPECT_EQ((int)(uintptr_t)extref_prop, 1000); dyntype_release(ctx, extref); dyntype_release(ctx, obj); dyntype_release(ctx, num); dyntype_release(ctx, boolean); dyntype_release(ctx, undefined); dyntype_release(ctx, null); dyntype_release(ctx, str); dyntype_release(ctx, array); dyntype_release(ctx, obj1); dyntype_release(ctx, desc1); dyntype_release(ctx, desc2); dyntype_release(ctx, desc3); dyntype_release(ctx, desc4); dyntype_release(ctx, desc5); dyntype_release(ctx, desc6); dyntype_release(ctx, desc7); dyntype_release(ctx, desc8); #if WASM_ENABLE_STRINGREF != 0 wasm_string_destroy(wasm_string); #endif } TEST_F(ObjectPropertyTest, object_set_and_delete_property) { dyn_value_t obj = dyntype_new_object(ctx); int ext_data = 1000; dyn_value_t num = dyntype_new_number(ctx, 2147483649); dyn_value_t boolean = dyntype_new_boolean(ctx, true); dyn_value_t undefined = dyntype_new_undefined(ctx); dyn_value_t null = dyntype_new_null(ctx); #if WASM_ENABLE_STRINGREF != 0 WASMString wasm_string = wasm_string_new_const("string", strlen("string")); dyn_value_t str = dyntype_new_string(ctx, wasm_string); #else dyn_value_t str = dyntype_new_string(ctx, "string", strlen("string")); #endif dyn_value_t array = dyntype_new_array(ctx, 0); EXPECT_TRUE(dyntype_is_array(ctx, array)); dyn_value_t extref = dyntype_new_extref(ctx, (void *)(uintptr_t)ext_data, external_ref_tag::ExtObj, NULL); dyn_value_t obj1 = dyntype_new_object(ctx); EXPECT_EQ(dyntype_set_property(ctx, obj, "prop1", num), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_set_property(ctx, obj, "prop2", boolean), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_set_property(ctx, obj, "prop3", undefined), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_set_property(ctx, obj, "prop4", null), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_set_property(ctx, obj, "prop5", str), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_set_property(ctx, obj, "prop6", array), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_set_property(ctx, obj, "prop7", extref), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_set_property(ctx, obj, "prop8", obj1), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_delete_property(ctx, obj, "prop1"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_delete_property(ctx, obj, "prop2"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_delete_property(ctx, obj, "prop3"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_delete_property(ctx, obj, "prop4"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_delete_property(ctx, obj, "prop5"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_delete_property(ctx, obj, "prop6"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_delete_property(ctx, obj, "prop7"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_delete_property(ctx, obj, "prop8"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_delete_property(ctx, obj, "prop9"), DYNTYPE_FALSE); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop1"), DYNTYPE_FALSE); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop2"), DYNTYPE_FALSE); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop3"), DYNTYPE_FALSE); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop4"), DYNTYPE_FALSE); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop5"), DYNTYPE_FALSE); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop6"), DYNTYPE_FALSE); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop7"), DYNTYPE_FALSE); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop8"), DYNTYPE_FALSE); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop9"), DYNTYPE_FALSE); dyntype_release(ctx, obj); dyntype_release(ctx, num); dyntype_release(ctx, boolean); dyntype_release(ctx, undefined); dyntype_release(ctx, null); dyntype_release(ctx, str); dyntype_release(ctx, array); dyntype_release(ctx, extref); dyntype_release(ctx, obj1); #if WASM_ENABLE_STRINGREF != 0 wasm_string_destroy(wasm_string); #endif } TEST_F(ObjectPropertyTest, object_define_and_delete_property) { dyn_value_t obj = dyntype_new_object(ctx); int ext_data = 1000; dyn_value_t num = dyntype_new_number(ctx, -10.1); dyn_value_t boolean = dyntype_new_boolean(ctx, true); dyn_value_t undefined = dyntype_new_undefined(ctx); dyn_value_t null = dyntype_new_null(ctx); #if WASM_ENABLE_STRINGREF != 0 WASMString wasm_string = wasm_string_new_const(" ", 2); dyn_value_t str = dyntype_new_string(ctx, wasm_string); #else dyn_value_t str = dyntype_new_string(ctx, " ", 2); #endif dyn_value_t array = dyntype_new_array(ctx, 0); dyn_value_t extref = dyntype_new_extref(ctx, (void *)(uintptr_t)ext_data, external_ref_tag::ExtObj, NULL); dyn_value_t obj1 = dyntype_new_object(ctx); dyn_value_t desc1 = dyntype_new_object(ctx); dyn_value_t desc1_v = dyntype_new_boolean(ctx, true); dyntype_set_property(ctx, desc1, "configurable", desc1_v); dyntype_set_property(ctx, desc1, "value", num); dyntype_release(ctx, desc1_v); dyn_value_t desc2 = dyntype_new_object(ctx); dyn_value_t desc2_v = dyntype_new_boolean(ctx, true); dyntype_set_property(ctx, desc2, "writable", desc2_v); dyntype_set_property(ctx, desc2, "value", boolean); dyntype_release(ctx, desc2_v); dyn_value_t desc3 = dyntype_new_object(ctx); dyn_value_t desc3_v = dyntype_new_boolean(ctx, true); dyntype_set_property(ctx, desc3, "enumerable", desc3_v); dyntype_set_property(ctx, desc3, "value", undefined); dyntype_release(ctx, desc3_v); dyn_value_t desc4 = dyntype_new_object(ctx); dyn_value_t desc4_v = dyntype_new_boolean(ctx, false); dyntype_set_property(ctx, desc4, "configurable", desc4_v); dyntype_set_property(ctx, desc4, "value", null); dyntype_release(ctx, desc4_v); dyn_value_t desc5 = dyntype_new_object(ctx); dyn_value_t desc5_v = dyntype_new_boolean(ctx, false); dyntype_set_property(ctx, desc5, "writable", desc5_v); dyntype_set_property(ctx, desc5, "value", str); dyntype_release(ctx, desc5_v); dyn_value_t desc6 = dyntype_new_object(ctx); dyn_value_t desc6_v = dyntype_new_boolean(ctx, false); dyntype_set_property(ctx, desc6, "enumerable", desc6_v); dyntype_set_property(ctx, desc6, "value", array); dyntype_release(ctx, desc6_v); dyn_value_t desc7 = dyntype_new_object(ctx); dyn_value_t desc7_v = dyntype_new_boolean(ctx, true); dyntype_set_property(ctx, desc7, "configurable", desc7_v); dyntype_set_property(ctx, desc7, "value", extref); dyntype_release(ctx, desc7_v); dyn_value_t desc8 = dyntype_new_object(ctx); dyn_value_t desc8_v = dyntype_new_boolean(ctx, true); dyntype_set_property(ctx, desc8, "writable", desc8_v); dyntype_set_property(ctx, desc8, "value", obj1); dyntype_release(ctx, desc8_v); EXPECT_EQ(dyntype_define_property(ctx, obj, "prop1", desc1), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_define_property(ctx, obj, "prop2", desc2), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_define_property(ctx, obj, "prop3", desc3), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_define_property(ctx, obj, "prop4", desc4), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_define_property(ctx, obj, "prop5", desc5), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_define_property(ctx, obj, "prop6", desc6), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_define_property(ctx, obj, "prop7", desc7), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_define_property(ctx, obj, "prop8", desc8), DYNTYPE_SUCCESS); EXPECT_EQ(dyntype_delete_property(ctx, obj, "prop1"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_delete_property(ctx, obj, "prop2"), DYNTYPE_FALSE); // writable EXPECT_EQ(dyntype_delete_property(ctx, obj, "prop3"), DYNTYPE_FALSE); EXPECT_EQ(dyntype_delete_property(ctx, obj, "prop4"), DYNTYPE_FALSE); EXPECT_EQ(dyntype_delete_property(ctx, obj, "prop5"), DYNTYPE_FALSE); EXPECT_EQ(dyntype_delete_property(ctx, obj, "prop6"), DYNTYPE_FALSE); EXPECT_EQ(dyntype_delete_property(ctx, obj, "prop7"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_delete_property(ctx, obj, "prop8"), DYNTYPE_FALSE); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop1"), DYNTYPE_FALSE); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop2"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop3"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop4"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop5"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop6"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop7"), DYNTYPE_FALSE); EXPECT_EQ(dyntype_has_property(ctx, obj, "prop8"), DYNTYPE_TRUE); void *extref_prop = nullptr; EXPECT_EQ(dyntype_to_extref(ctx, extref, &extref_prop), DYNTYPE_SUCCESS); EXPECT_EQ((int)(uintptr_t)extref_prop, 1000); dyntype_release(ctx, obj); dyntype_release(ctx, num); dyntype_release(ctx, boolean); dyntype_release(ctx, undefined); dyntype_release(ctx, null); dyntype_release(ctx, str); dyntype_release(ctx, array); dyntype_release(ctx, extref); dyntype_release(ctx, obj1); dyntype_release(ctx, desc1); dyntype_release(ctx, desc2); dyntype_release(ctx, desc3); dyntype_release(ctx, desc4); dyntype_release(ctx, desc5); dyntype_release(ctx, desc6); dyntype_release(ctx, desc7); dyntype_release(ctx, desc8); #if WASM_ENABLE_STRINGREF != 0 wasm_string_destroy(wasm_string); #endif } TEST_F(ObjectPropertyTest, map_function_test) { dyn_value_t obj = dyntype_new_object_with_class(ctx, "Map", 0, NULL); dyn_value_t num = dyntype_new_number(ctx, -10.1); dyn_value_t boolean = dyntype_new_boolean(ctx, true); #if WASM_ENABLE_STRINGREF != 0 WASMString wasm_string = wasm_string_new_const("123", strlen("123")); dyn_value_t str = dyntype_new_string(ctx, wasm_string); #else dyn_value_t str = dyntype_new_string(ctx, "123", strlen("123")); #endif dyn_value_t array = dyntype_new_array(ctx, 0); dyn_value_t argv[10]; EXPECT_EQ(dyntype_has_property(ctx, obj, "set"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_has_property(ctx, obj, "get"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_has_property(ctx, obj, "has"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_has_property(ctx, obj, "delete"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_has_property(ctx, obj, "clear"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_has_property(ctx, obj, "size"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_has_property(ctx, obj, "forEach"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_has_property(ctx, obj, "values"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_has_property(ctx, obj, "keys"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_has_property(ctx, obj, "entries"), DYNTYPE_TRUE); argv[0] = num; argv[1] = boolean; argv[2] = str; argv[3] = array; dyn_value_t ret; ret = dyntype_invoke(ctx, "set", obj, 2, argv); // set num -> boolean dyntype_release(ctx, ret); // release duplicate map ret = dyntype_invoke(ctx, "set", obj, 2, argv + 2); // set str -> array dyntype_release(ctx, ret); // release duplicate map ret = dyntype_invoke(ctx, "has", obj, 1, argv); bool has; dyntype_to_bool(ctx, ret, &has); EXPECT_EQ(has, DYNTYPE_TRUE); // set success dyntype_release(ctx, ret); ret = dyntype_get_property(ctx, obj, "size"); double sz; dyntype_to_number(ctx, ret, &sz); EXPECT_EQ(sz, 2.0); // size is 2 dyntype_release(ctx, ret); ret = dyntype_invoke(ctx, "delete", obj, 1, argv); // delete num -> boolean dyntype_to_bool(ctx, ret, &has); EXPECT_EQ(has, DYNTYPE_TRUE); // delete success dyntype_release(ctx, ret); ret = dyntype_get_property(ctx, obj, "size"); dyntype_to_number(ctx, ret, &sz); EXPECT_EQ(sz, 1.0); // size is 1 dyntype_release(ctx, ret); ret = dyntype_invoke(ctx, "clear", obj, 0, argv); // clear dyntype_release(ctx, ret); ret = dyntype_get_property(ctx, obj, "size"); dyntype_to_number(ctx, ret, &sz); EXPECT_EQ(sz, 0.0); // size is 0 dyntype_release(ctx, ret); dyntype_release(ctx, num); dyntype_release(ctx, boolean); dyntype_release(ctx, str); dyntype_release(ctx, array); dyntype_release(ctx, obj); #if WASM_ENABLE_STRINGREF != 0 wasm_string_destroy(wasm_string); #endif } static dyn_value_t test_callback_dispatcher(void *exec_env_v, dyn_ctx_t ctx, void *vfunc, dyn_value_t this_obj, int argc, dyn_value_t *args) { return dyntype_new_boolean(dyntype_get_context(), true); } TEST_F(ObjectPropertyTest, map_callback_test) { wasm_module_t wasm_module; wasm_module_inst_t module_inst; wasm_exec_env_t exec_env; dyn_value_t obj = dyntype_new_object_with_class(ctx, "Map", 0, NULL); wasm_runtime_init(); wasm_module = wasm_runtime_load(test_app, sizeof(test_app), NULL, 0); EXPECT_TRUE(wasm_module != NULL); module_inst = wasm_runtime_instantiate(wasm_module, 8192, 1024, NULL, 0); EXPECT_TRUE(module_inst != NULL); exec_env = wasm_runtime_create_exec_env(module_inst, 4096); EXPECT_TRUE(exec_env != NULL); dyntype_context_set_exec_env(exec_env); char str[] = { ' ', '\0' }; dyn_value_t argv[10]; dyn_value_t gkey; dyn_value_t ret; for (int i = 0, j = '0'; i < 10; i++, j++) { str[0] = j; #if WASM_ENABLE_STRINGREF != 0 WASMString wasm_string = wasm_string_new_const(str, strlen(str)); dyn_value_t key = dyntype_new_string(ctx, wasm_string); #else dyn_value_t key = dyntype_new_string(ctx, str, strlen(str)); #endif dyn_value_t val = dyntype_new_number(ctx, i); argv[0] = key; argv[1] = val; ret = dyntype_invoke(ctx, "set", obj, 2, argv); // set num -> boolean dyntype_release(ctx, ret); // release duplicate map dyntype_release(ctx, key); dyntype_release(ctx, val); #if WASM_ENABLE_STRINGREF != 0 wasm_string_destroy(wasm_string); #endif } #if WASM_ENABLE_STRINGREF != 0 WASMString wasm_string = wasm_string_new_const(str, strlen(str)); gkey = dyntype_new_string(ctx, wasm_string); wasm_string_destroy(wasm_string); #else gkey = dyntype_new_string(ctx, str, strlen(str)); #endif argv[0] = gkey; ret = dyntype_invoke(ctx, "has", obj, 1, argv); bool has; dyntype_to_bool(ctx, ret, &has); EXPECT_EQ(has, DYNTYPE_TRUE); // set success dyntype_release(ctx, ret); dyntype_release(ctx, gkey); ret = dyntype_get_property(ctx, obj, "size"); double sz; dyntype_to_number(ctx, ret, &sz); EXPECT_EQ(sz, 10.0); // size is 10 dyn_value_t func = dyntype_new_extref(ctx, NULL, ExtFunc, NULL); EXPECT_EQ(dyntype_is_function(ctx, func), DYNTYPE_TRUE); dyntype_release(ctx, ret); argv[0] = func; ret = dyntype_invoke(ctx, "forEach", obj, 1, argv); /* We didn't register a callback dispatcher, so the return value should be * exception */ EXPECT_TRUE(dyntype_is_exception(ctx, ret)); dyntype_release(ctx, ret); dyntype_set_callback_dispatcher(test_callback_dispatcher); ret = dyntype_invoke(ctx, "forEach", obj, 1, argv); /* The forEach method should return undefined no matter what is returned by * the callback */ EXPECT_TRUE(dyntype_is_undefined(ctx, ret)); dyntype_release(ctx, ret); dyntype_release(ctx, func); dyntype_release(ctx, obj); wasm_runtime_destroy_exec_env(exec_env); wasm_runtime_deinstantiate(module_inst); wasm_runtime_unload(wasm_module); } TEST_F(ObjectPropertyTest, get_keys) { dyn_value_t obj = dyntype_new_object(ctx); dyn_value_t property_value = dyntype_new_number(ctx, 100); dyn_value_t keys = NULL; dyn_value_t length_property = NULL; double arr_length; dyntype_set_property(ctx, obj, "a", property_value); keys = dyntype_get_keys(ctx, obj); EXPECT_TRUE(dyntype_is_array(ctx, keys)); length_property = dyntype_get_property(ctx, keys, "length"); dyntype_to_number(ctx, length_property, &arr_length); EXPECT_EQ(arr_length, 1.0); dyntype_release(ctx, property_value); dyntype_release(ctx, keys); dyntype_release(ctx, obj); dyntype_release(ctx, length_property); } ================================================ FILE: runtime-library/libdyntype/test/operator_test.cc ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "libdyntype_export.h" #include "stringref/string_object.h" #include class OperatorTest : public testing::Test { protected: virtual void SetUp() { ctx = dyntype_context_init(); } virtual void TearDown() { dyntype_context_destroy(ctx); } testing::AssertionResult is_type_eq(dyn_value_t lhs, dyn_value_t rhs, uint32_t l, uint32_t r) { if (dyntype_type_eq(ctx, lhs, rhs)) { return testing::AssertionSuccess() << "they are value1[" << l << "], value2[" << r << "]"; } return testing::AssertionFailure() << "they are value1[" << l << "], value2[" << r << "]"; } dyn_ctx_t ctx; }; TEST_F(OperatorTest, typeof) { int ext_data = 1000; dyn_value_t num = dyntype_new_number(ctx, 2147483649); dyn_value_t boolean = dyntype_new_boolean(ctx, true); dyn_value_t undefined = dyntype_new_undefined(ctx); dyn_value_t null = dyntype_new_null(ctx); dyn_value_t obj = dyntype_new_object(ctx); #if WASM_ENABLE_STRINGREF != 0 WASMString wasm_string = wasm_string_new_const("string", strlen("string")); dyn_value_t str = dyntype_new_string(ctx, wasm_string); #else dyn_value_t str = dyntype_new_string(ctx, "string", strlen("string")); #endif dyn_value_t array = dyntype_new_array(ctx, 0); dyn_value_t extref_obj = dyntype_new_extref( ctx, (void *)(uintptr_t)ext_data, external_ref_tag::ExtObj, NULL); dyn_value_t extref_func = dyntype_new_extref( ctx, (void *)(uintptr_t)ext_data, external_ref_tag::ExtFunc, NULL); EXPECT_EQ(dyntype_typeof(ctx, num), DynNumber); EXPECT_EQ(dyntype_typeof(ctx, boolean), DynBoolean); EXPECT_EQ(dyntype_typeof(ctx, undefined), DynUndefined); EXPECT_EQ(dyntype_typeof(ctx, null), DynObject); EXPECT_EQ(dyntype_typeof(ctx, obj), DynObject); EXPECT_EQ(dyntype_typeof(ctx, str), DynString); EXPECT_EQ(dyntype_typeof(ctx, array), DynObject); EXPECT_EQ(dyntype_typeof(ctx, extref_obj), DynExtRefObj); EXPECT_EQ(dyntype_typeof(ctx, extref_func), DynExtRefFunc); dyntype_release(ctx, num); dyntype_release(ctx, boolean); dyntype_release(ctx, obj); dyntype_release(ctx, str); dyntype_release(ctx, array); dyntype_release(ctx, extref_obj); dyntype_release(ctx, extref_func); #if WASM_ENABLE_STRINGREF != 0 wasm_string_destroy(wasm_string); #endif } TEST_F(OperatorTest, type_eq) { int ext_data = 1000; #if WASM_ENABLE_STRINGREF != 0 WASMString wasm_string1 = wasm_string_new_const("string", strlen("string")); WASMString wasm_string2 = wasm_string_new_const("test", strlen("test")); #endif dyn_value_t value1[] = { dyntype_new_number(ctx, 2147483649), dyntype_new_boolean(ctx, true), dyntype_new_undefined(ctx), #if WASM_ENABLE_STRINGREF != 0 dyntype_new_string(ctx, wasm_string1), #else dyntype_new_string(ctx, "string", strlen("string")), #endif dyntype_new_extref(ctx, (void *)(uintptr_t)ext_data, external_ref_tag::ExtObj, NULL), dyntype_new_extref(ctx, (void *)(uintptr_t)ext_data, external_ref_tag::ExtFunc, NULL) }; dyn_value_t value2[] = { dyntype_new_number(ctx, -10.00), dyntype_new_boolean(ctx, false), dyntype_new_undefined(ctx), #if WASM_ENABLE_STRINGREF != 0 dyntype_new_string(ctx, wasm_string2), #else dyntype_new_string(ctx, "test", strlen("test")), #endif dyntype_new_extref(ctx, (void *)(uintptr_t)ext_data, external_ref_tag::ExtObj, NULL), dyntype_new_extref(ctx, (void *)(uintptr_t)ext_data, external_ref_tag::ExtFunc, NULL), dyntype_new_null(ctx), dyntype_new_object(ctx), dyntype_new_array(ctx, 0) }; // they are all object type dyn_value_t value3[] = { dyntype_new_null(ctx), dyntype_new_object(ctx), dyntype_new_array(ctx, 0) }; uint32_t len1 = sizeof(value1) / sizeof(dyn_value_t); uint32_t len2 = sizeof(value2) / sizeof(dyn_value_t); uint32_t len3 = sizeof(value3) / sizeof(dyn_value_t); for (uint32_t i = 0; i < len1; i++) { for (uint32_t j = 0; j < len2; j++) { if (i == j) { EXPECT_TRUE(is_type_eq(value1[i], value2[j], i, j)); continue; } EXPECT_FALSE(is_type_eq(value1[i], value2[j], i, j)); } } // null, arary, object types for (uint32_t i = 8; i < len2; i++) { for (uint32_t j = 8; j < len3; j++) { EXPECT_TRUE(is_type_eq(value2[i], value3[j], i, j)); } } for (uint32_t i = 0; i < len1; i++) { if (value1[i] == dyntype_new_undefined(ctx) || value1[i] == dyntype_new_null(ctx)) { continue; } dyntype_release(ctx, value1[i]); } for (uint32_t i = 0; i < len2; i++) { if (value2[i] == dyntype_new_undefined(ctx) || value2[i] == dyntype_new_null(ctx)) { continue; } dyntype_release(ctx, value2[i]); } for (uint32_t i = 0; i < len3; i++) { if (value3[i] == dyntype_new_undefined(ctx) || value3[i] == dyntype_new_null(ctx)) { continue; } dyntype_release(ctx, value3[i]); } #if WASM_ENABLE_STRINGREF != 0 wasm_string_destroy(wasm_string1); wasm_string_destroy(wasm_string2); #endif } ================================================ FILE: runtime-library/libdyntype/test/prototype_test.cc ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "libdyntype_export.h" #include "stringref/string_object.h" #include "wasm.h" #include class PrototypeTest : public testing::Test { protected: virtual void SetUp() { ctx = dyntype_context_init(); } virtual void TearDown() { dyntype_context_destroy(ctx); } dyn_ctx_t ctx; }; TEST_F(PrototypeTest, prototype) { char const *name = "Jack"; dyn_value_t num; dyn_value_t obj1 = dyntype_new_object(ctx); EXPECT_NE(obj1, nullptr); #if WASM_ENABLE_STRINGREF != 0 WASMString wasm_string = wasm_string_new_const(name, strlen(name)); dyn_value_t prop1 = dyntype_new_string(ctx, wasm_string); #else dyn_value_t prop1 = dyntype_new_string(ctx, name, strlen(name)); #endif EXPECT_NE(prop1, nullptr); EXPECT_EQ(dyntype_set_property(ctx, obj1, "name", prop1), DYNTYPE_SUCCESS); dyntype_release(ctx, prop1); num = dyntype_new_number(ctx, 10.0); EXPECT_EQ(dyntype_new_object_with_proto(ctx, num), nullptr); dyntype_release(ctx, num); dyn_value_t obj2 = dyntype_new_object_with_proto(ctx, obj1); EXPECT_NE(obj2, nullptr); EXPECT_EQ(dyntype_has_property(ctx, obj2, "name"), DYNTYPE_TRUE); EXPECT_FALSE(dyntype_instanceof(ctx, obj1, obj2)); dyn_value_t value = dyntype_get_property(ctx, obj2, "name"); EXPECT_TRUE(dyntype_is_string(ctx, value)); char *raw_value = nullptr; EXPECT_EQ(dyntype_to_cstring(ctx, value, &raw_value), DYNTYPE_SUCCESS); EXPECT_STREQ(raw_value, name); dyntype_free_cstring(ctx, raw_value); dyntype_release(ctx, value); dyn_value_t own_property = dyntype_get_own_property(ctx, obj2, "name"); EXPECT_EQ(own_property, nullptr); dyntype_release(ctx, own_property); dyn_value_t obj3 = dyntype_new_object(ctx); EXPECT_EQ(dyntype_set_prototype(ctx, obj1, obj3), DYNTYPE_SUCCESS); num = dyntype_new_number(ctx, 10.0); EXPECT_EQ(dyntype_set_prototype(ctx, obj1, num), -DYNTYPE_TYPEERR); dyntype_release(ctx, num); // TODO: cant test now // dyntype_instanceof(ctx) dyntype_release(ctx, obj1); dyntype_release(ctx, obj2); dyntype_release(ctx, obj3); #if WASM_ENABLE_STRINGREF != 0 wasm_string_destroy(wasm_string); #endif } ================================================ FILE: runtime-library/libdyntype/test/test_app.h ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ unsigned char test_app[] = { 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x01, 0x44, 0x0C, 0x5E, 0x78, 0x01, 0x5E, 0x6E, 0x01, 0x50, 0x00, 0x5F, 0x00, 0x50, 0x01, 0x02, 0x5F, 0x02, 0x63, 0x01, 0x01, 0x7F, 0x01, 0x60, 0x00, 0x01, 0x6E, 0x60, 0x02, 0x6E, 0x6E, 0x01, 0x6E, 0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F, 0x60, 0x02, 0x63, 0x02, 0x63, 0x03, 0x00, 0x60, 0x01, 0x6E, 0x01, 0x7F, 0x60, 0x03, 0x7F, 0x7F, 0x7F, 0x01, 0x7F, 0x60, 0x03, 0x63, 0x02, 0x63, 0x02, 0x63, 0x03, 0x00, 0x60, 0x00, 0x00, 0x02, 0x61, 0x04, 0x0A, 0x6C, 0x69, 0x62, 0x64, 0x79, 0x6E, 0x74, 0x79, 0x70, 0x65, 0x13, 0x64, 0x79, 0x6E, 0x74, 0x79, 0x70, 0x65, 0x5F, 0x67, 0x65, 0x74, 0x5F, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x00, 0x04, 0x0A, 0x6C, 0x69, 0x62, 0x64, 0x79, 0x6E, 0x74, 0x79, 0x70, 0x65, 0x12, 0x64, 0x79, 0x6E, 0x74, 0x79, 0x70, 0x65, 0x5F, 0x6E, 0x65, 0x77, 0x5F, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x00, 0x05, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x73, 0x74, 0x72, 0x63, 0x6D, 0x70, 0x00, 0x06, 0x03, 0x65, 0x6E, 0x76, 0x0B, 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x5F, 0x6C, 0x6F, 0x67, 0x00, 0x07, 0x03, 0x05, 0x04, 0x08, 0x09, 0x0A, 0x0B, 0x04, 0x04, 0x01, 0x6E, 0x00, 0x00, 0x05, 0x04, 0x01, 0x01, 0x01, 0x0A, 0x06, 0x18, 0x04, 0x6E, 0x01, 0xD0, 0x71, 0x0B, 0x63, 0x00, 0x01, 0xD0, 0x71, 0x0B, 0x7F, 0x00, 0x41, 0xFC, 0x08, 0x0B, 0x7F, 0x00, 0x41, 0xFC, 0x10, 0x0B, 0x07, 0x65, 0x06, 0x14, 0x61, 0x6C, 0x6C, 0x6F, 0x63, 0x45, 0x78, 0x74, 0x52, 0x65, 0x66, 0x54, 0x61, 0x62, 0x6C, 0x65, 0x53, 0x6C, 0x6F, 0x74, 0x00, 0x04, 0x1C, 0x66, 0x69, 0x6E, 0x64, 0x5F, 0x70, 0x72, 0x6F, 0x70, 0x65, 0x72, 0x74, 0x79, 0x5F, 0x66, 0x6C, 0x61, 0x67, 0x5F, 0x61, 0x6E, 0x64, 0x5F, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x00, 0x05, 0x06, 0x5F, 0x65, 0x6E, 0x74, 0x72, 0x79, 0x00, 0x07, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x02, 0x00, 0x0A, 0x5F, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x5F, 0x65, 0x6E, 0x64, 0x03, 0x02, 0x0B, 0x5F, 0x5F, 0x68, 0x65, 0x61, 0x70, 0x5F, 0x62, 0x61, 0x73, 0x65, 0x03, 0x03, 0x0C, 0x01, 0x01, 0x0A, 0xCD, 0x02, 0x04, 0x97, 0x01, 0x03, 0x02, 0x7F, 0x01, 0x63, 0x00, 0x01, 0x64, 0x00, 0x41, 0x7F, 0x21, 0x02, 0x23, 0x01, 0xD1, 0x04, 0x40, 0xD0, 0x71, 0x41, 0x0A, 0xFC, 0x0F, 0x00, 0x1A, 0x41, 0x00, 0xFC, 0x10, 0x00, 0xFB, 0x06, 0x00, 0x24, 0x01, 0x0B, 0x03, 0x40, 0x20, 0x01, 0x23, 0x01, 0x22, 0x03, 0xFB, 0x0F, 0x49, 0x04, 0x40, 0x02, 0x40, 0x20, 0x03, 0x20, 0x01, 0xFB, 0x0D, 0x00, 0x45, 0x04, 0x40, 0x20, 0x01, 0x21, 0x02, 0x0C, 0x01, 0x0B, 0x20, 0x01, 0x41, 0x01, 0x6A, 0x21, 0x01, 0x0C, 0x02, 0x0B, 0x0B, 0x0B, 0x20, 0x02, 0x41, 0x7F, 0x46, 0x04, 0x40, 0xD0, 0x71, 0x41, 0x0A, 0xFC, 0x0F, 0x00, 0x1A, 0x23, 0x01, 0x22, 0x03, 0xFB, 0x0F, 0x22, 0x01, 0x21, 0x02, 0x41, 0x00, 0xFC, 0x10, 0x00, 0xFB, 0x06, 0x00, 0x22, 0x04, 0x41, 0x00, 0x20, 0x03, 0x41, 0x00, 0x20, 0x01, 0xFB, 0x11, 0x00, 0x00, 0x20, 0x04, 0x24, 0x01, 0x0B, 0x20, 0x02, 0x20, 0x00, 0x26, 0x00, 0x23, 0x01, 0x20, 0x02, 0x41, 0x01, 0xFB, 0x0E, 0x00, 0x20, 0x02, 0x0B, 0x68, 0x01, 0x04, 0x7F, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x08, 0x22, 0x03, 0x41, 0x00, 0x4A, 0x04, 0x40, 0x20, 0x00, 0x41, 0x10, 0x6A, 0x21, 0x00, 0x20, 0x02, 0x41, 0x0F, 0x71, 0x22, 0x04, 0x41, 0x04, 0x46, 0x21, 0x05, 0x03, 0x40, 0x20, 0x00, 0x28, 0x02, 0x00, 0x21, 0x02, 0x20, 0x00, 0x41, 0x04, 0x6B, 0x28, 0x02, 0x00, 0x20, 0x01, 0x10, 0x02, 0x45, 0x04, 0x40, 0x20, 0x05, 0x0D, 0x03, 0x20, 0x02, 0x41, 0x0F, 0x71, 0x20, 0x04, 0x46, 0x0D, 0x03, 0x0B, 0x20, 0x00, 0x41, 0x0C, 0x6A, 0x21, 0x00, 0x20, 0x06, 0x41, 0x01, 0x6A, 0x22, 0x06, 0x20, 0x03, 0x48, 0x0D, 0x00, 0x0B, 0x0B, 0x41, 0x7F, 0x21, 0x02, 0x0B, 0x20, 0x02, 0x0B, 0x08, 0x00, 0x20, 0x01, 0x20, 0x02, 0x10, 0x03, 0x0B, 0x40, 0x01, 0x02, 0x64, 0x01, 0x10, 0x00, 0x24, 0x00, 0x23, 0x00, 0x41, 0xF8, 0x08, 0x41, 0x00, 0xFB, 0x80, 0x01, 0x00, 0x10, 0x01, 0xFB, 0x08, 0x01, 0x01, 0x22, 0x00, 0xFB, 0x0F, 0xFB, 0x07, 0x01, 0x22, 0x01, 0x41, 0x00, 0x20, 0x00, 0x41, 0x00, 0x20, 0x00, 0xFB, 0x0F, 0xFB, 0x11, 0x01, 0x01, 0xD0, 0x71, 0xD0, 0x71, 0x20, 0x01, 0x20, 0x01, 0xFB, 0x0F, 0xFB, 0x00, 0x03, 0x10, 0x06, 0x0B, 0x0B, 0x7C, 0x01, 0x00, 0x41, 0x80, 0x08, 0x0B, 0x75, 0x6C, 0x6F, 0x67, 0x00, 0x38, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x70, 0x6F, 0x77, 0x00, 0x6D, 0x61, 0x78, 0x00, 0x6D, 0x69, 0x6E, 0x00, 0x56, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x1C, 0x04, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x24, 0x04, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x69, 0x73, 0x56, 0x69, 0x65, 0x77, 0x00, 0x00, 0x58, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x58, 0x04, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x18 }; ================================================ FILE: runtime-library/libdyntype/test/types_test.cc ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "libdyntype_export.h" #include "string_object.h" #include class TypesTest : public testing::Test { protected: virtual void SetUp() { ctx = dyntype_context_init(); if (ctx == NULL) { } } virtual void TearDown() { dyntype_context_destroy(ctx); } dyn_ctx_t ctx; }; TEST_F(TypesTest, is_undefined) { dyn_value_t boolean = dyntype_new_boolean(ctx, false); EXPECT_FALSE(dyntype_is_undefined(ctx, boolean)); dyntype_release(ctx, boolean); dyn_value_t number = dyntype_new_number(ctx, 0); EXPECT_FALSE(dyntype_is_undefined(ctx, number)); dyntype_release(ctx, number); dyn_value_t obj = dyntype_new_object(ctx); EXPECT_FALSE(dyntype_is_undefined(ctx, obj)); dyntype_release(ctx, obj); dyn_value_t undefined = dyntype_new_undefined(ctx); EXPECT_TRUE(dyntype_is_undefined(ctx, undefined)); } TEST_F(TypesTest, create_number_object) { double check_values[] = { -1, 0, 0x100, 0x1000, 0x3fffffff, 0x7ffffffe, 0x7ffffff, 0x80000000, 0xfffffffe, 0xffffffff, 0x10000, 0x100000, 2147483649.1, -5.48, 1234.0 }; for (int i = 0; i < sizeof(check_values) / sizeof(check_values[0]); i++) { double raw_number = 0; dyn_value_t num = dyntype_new_number(ctx, check_values[i]); EXPECT_NE(num, nullptr); dyntype_dump_value(ctx, num); dyn_value_t prop1 = dyntype_new_boolean(ctx, false); dyn_value_t prop2 = dyntype_new_boolean(ctx, false); EXPECT_EQ(dyntype_set_property(ctx, num, "not_a_object", prop1), -DYNTYPE_TYPEERR); EXPECT_EQ(dyntype_define_property(ctx, num, "not_a_object", prop2), -DYNTYPE_TYPEERR); dyntype_release(ctx, prop1); dyntype_release(ctx, prop2); dyn_value_t prop = dyntype_get_property(ctx, num, "not_a_object"); EXPECT_TRUE(dyntype_is_undefined(ctx, prop)); EXPECT_EQ(dyntype_has_property(ctx, num, "not_a_object"), -DYNTYPE_TYPEERR); EXPECT_EQ(dyntype_delete_property(ctx, num, "not_a_object"), -DYNTYPE_FALSE); EXPECT_TRUE(dyntype_is_number(ctx, num)); EXPECT_FALSE(dyntype_is_bool(ctx, num)); EXPECT_FALSE(dyntype_is_object(ctx, num)); EXPECT_FALSE(dyntype_is_undefined(ctx, num)); EXPECT_FALSE(dyntype_is_null(ctx, num)); EXPECT_FALSE(dyntype_is_string(ctx, num)); EXPECT_FALSE(dyntype_is_array(ctx, num)); EXPECT_FALSE(dyntype_is_extref(ctx, num)); bool temp; char *temp2; EXPECT_EQ(dyntype_to_bool(ctx, num, &temp), -DYNTYPE_TYPEERR); EXPECT_EQ(dyntype_to_cstring(ctx, num, &temp2), DYNTYPE_SUCCESS); dyntype_free_cstring(ctx, temp2); dyntype_to_number(ctx, num, &raw_number); EXPECT_EQ(raw_number, check_values[i]); dyntype_release(ctx, num); } } TEST_F(TypesTest, create_boolean_object) { bool check_values[] = { true, false, false, false, true }; for (int i = 0; i < sizeof(check_values) / sizeof(check_values[0]); i++) { bool raw_value = 0; dyn_value_t boolean = dyntype_new_boolean(ctx, check_values[i]); EXPECT_NE(boolean, nullptr); dyn_value_t prop1 = dyntype_new_boolean(ctx, false); dyn_value_t prop2 = dyntype_new_boolean(ctx, false); EXPECT_EQ(dyntype_set_property(ctx, boolean, "not_a_object", prop1), -DYNTYPE_TYPEERR); EXPECT_EQ(dyntype_define_property(ctx, boolean, "not_a_object", prop2), -DYNTYPE_TYPEERR); dyn_value_t prop = dyntype_get_property(ctx, boolean, "not_a_object"); EXPECT_TRUE(dyntype_is_undefined(ctx, prop)); EXPECT_EQ(dyntype_has_property(ctx, boolean, "not_a_object"), -DYNTYPE_TYPEERR); EXPECT_EQ(dyntype_delete_property(ctx, boolean, "not_a_object"), -DYNTYPE_FALSE); EXPECT_FALSE(dyntype_is_number(ctx, boolean)); EXPECT_TRUE(dyntype_is_bool(ctx, boolean)); EXPECT_FALSE(dyntype_is_object(ctx, boolean)); EXPECT_FALSE(dyntype_is_undefined(ctx, boolean)); EXPECT_FALSE(dyntype_is_null(ctx, boolean)); EXPECT_FALSE(dyntype_is_string(ctx, boolean)); EXPECT_FALSE(dyntype_is_array(ctx, boolean)); EXPECT_FALSE(dyntype_is_extref(ctx, boolean)); double temp1; char *temp2; EXPECT_EQ(dyntype_to_number(ctx, boolean, &temp1), -DYNTYPE_TYPEERR); EXPECT_EQ(dyntype_to_cstring(ctx, boolean, &temp2), DYNTYPE_SUCCESS); dyntype_to_bool(ctx, boolean, &raw_value); EXPECT_EQ(raw_value, check_values[i]); dyntype_release(ctx, prop1); dyntype_release(ctx, prop2); dyntype_release(ctx, boolean); } } TEST_F(TypesTest, create_undefined) { dyn_value_t undefined = dyntype_new_undefined(ctx); EXPECT_NE(undefined, nullptr); EXPECT_FALSE(dyntype_is_number(ctx, undefined)); EXPECT_FALSE(dyntype_is_bool(ctx, undefined)); EXPECT_FALSE(dyntype_is_object(ctx, undefined)); EXPECT_TRUE(dyntype_is_undefined(ctx, undefined)); EXPECT_FALSE(dyntype_is_null(ctx, undefined)); EXPECT_FALSE(dyntype_is_string(ctx, undefined)); EXPECT_FALSE(dyntype_is_array(ctx, undefined)); EXPECT_FALSE(dyntype_is_extref(ctx, undefined)); dyn_value_t prop = dyntype_new_boolean(ctx, false); EXPECT_EQ(dyntype_set_prototype(ctx, undefined, prop), -DYNTYPE_TYPEERR); EXPECT_EQ(dyntype_get_prototype(ctx, undefined), nullptr); EXPECT_EQ(dyntype_get_own_property(ctx, undefined, "has not property"), nullptr); dyntype_release(ctx, prop); bool temp; double temp1; char *temp2; EXPECT_EQ(dyntype_to_bool(ctx, undefined, &temp), -DYNTYPE_TYPEERR); EXPECT_EQ(dyntype_to_number(ctx, undefined, &temp1), -DYNTYPE_TYPEERR); EXPECT_EQ(dyntype_to_cstring(ctx, undefined, &temp2), DYNTYPE_SUCCESS); } TEST_F(TypesTest, create_null) { dyn_value_t null = dyntype_new_null(ctx); EXPECT_NE(null, nullptr); EXPECT_FALSE(dyntype_is_number(ctx, null)); EXPECT_FALSE(dyntype_is_bool(ctx, null)); EXPECT_FALSE(dyntype_is_object(ctx, null)); EXPECT_FALSE(dyntype_is_undefined(ctx, null)); EXPECT_TRUE(dyntype_is_null(ctx, null)); EXPECT_FALSE(dyntype_is_string(ctx, null)); EXPECT_FALSE(dyntype_is_array(ctx, null)); EXPECT_FALSE(dyntype_is_extref(ctx, null)); dyn_value_t prop = dyntype_new_boolean(ctx, false); EXPECT_EQ(dyntype_set_prototype(ctx, null, prop), -DYNTYPE_TYPEERR); EXPECT_EQ(dyntype_get_prototype(ctx, null), nullptr); EXPECT_EQ(dyntype_get_own_property(ctx, null, "has not property"), nullptr); dyntype_release(ctx, prop); } TEST_F(TypesTest, create_string) { char const *check_values[] = { "", " ", "abcd", "123456", "字符串", "@#$%^&*)(*", "terminal\0term" }; char const *validate_values[] = { "", " ", "abcd", "123456", "字符串", "@#$%^&*)(*", "terminal" }; for (int i = 0; i < sizeof(check_values) / sizeof(check_values[0]); i++) { char *raw_value = nullptr; #if WASM_ENABLE_STRINGREF != 0 WASMString wasm_string = wasm_string_new_const(check_values[i], strlen(check_values[i])); dyn_value_t str = dyntype_new_string(ctx, wasm_string); #else dyn_value_t str = dyntype_new_string(ctx, check_values[i], strlen(check_values[i])); #endif dyn_value_t str_dup; EXPECT_NE(str, nullptr); dyn_value_t prop1 = dyntype_new_boolean(ctx, false); dyn_value_t prop2 = dyntype_new_boolean(ctx, false); EXPECT_EQ(dyntype_set_property(ctx, str, "not_a_object", prop1), -DYNTYPE_TYPEERR); EXPECT_EQ(dyntype_define_property(ctx, str, "not_a_object", prop2), -DYNTYPE_TYPEERR); dyntype_release(ctx, prop1); dyntype_release(ctx, prop2); dyn_value_t prop = dyntype_get_property(ctx, str, "not_a_object"); EXPECT_TRUE(dyntype_is_undefined(ctx, prop)); dyntype_release(ctx, prop); EXPECT_EQ(dyntype_has_property(ctx, str, "not_a_object"), -DYNTYPE_TYPEERR); EXPECT_EQ(dyntype_delete_property(ctx, str, "not_a_object"), -DYNTYPE_FALSE); EXPECT_FALSE(dyntype_is_number(ctx, str)); EXPECT_FALSE(dyntype_is_bool(ctx, str)); EXPECT_FALSE(dyntype_is_object(ctx, str)); EXPECT_FALSE(dyntype_is_undefined(ctx, str)); EXPECT_FALSE(dyntype_is_null(ctx, str)); EXPECT_TRUE(dyntype_is_string(ctx, str)); EXPECT_FALSE(dyntype_is_array(ctx, str)); EXPECT_FALSE(dyntype_is_extref(ctx, str)); str_dup = dyntype_hold(ctx, str); dyntype_release(ctx, str_dup); bool temp; double temp1; EXPECT_EQ(dyntype_to_bool(ctx, str, &temp), -DYNTYPE_TYPEERR); EXPECT_EQ(dyntype_to_number(ctx, str, &temp1), -DYNTYPE_TYPEERR); EXPECT_EQ(dyntype_to_cstring(ctx, str, &raw_value), DYNTYPE_SUCCESS); EXPECT_STREQ(raw_value, validate_values[i]); dyntype_release(ctx, str); dyntype_free_cstring(ctx, raw_value); #if WASM_ENABLE_STRINGREF != 0 wasm_string_destroy(wasm_string); #endif } char const *str_values[] = { "", " ", "abc", "字符串", "123456", "@#$%^&*)(*" }; char const *cmp_values[] = { "", " ", "ab", "字", "1234", "@#$%^" }; // length from 0 to 5, '字' have 3 chars exactly. for (int i = 0; i < sizeof(str_values) / sizeof(str_values[0]); i++) { char *raw_value = nullptr; #if WASM_ENABLE_STRINGREF != 0 WASMString wasm_string = wasm_string_new_with_encoding((void *)str_values[i], i, WTF16); dyn_value_t str = dyntype_new_string(ctx, wasm_string); #else dyn_value_t str = dyntype_new_string(ctx, str_values[i], i); #endif dyn_value_t str_dup; EXPECT_NE(str, nullptr); dyn_value_t prop1 = dyntype_new_boolean(ctx, false); dyn_value_t prop2 = dyntype_new_boolean(ctx, false); EXPECT_EQ(dyntype_set_property(ctx, str, "not_a_object", prop1), -DYNTYPE_TYPEERR); EXPECT_EQ(dyntype_define_property(ctx, str, "not_a_object", prop2), -DYNTYPE_TYPEERR); dyntype_release(ctx, prop1); dyntype_release(ctx, prop2); dyn_value_t prop = dyntype_get_property(ctx, str, "not_a_object"); EXPECT_TRUE(dyntype_is_undefined(ctx, prop)); dyntype_release(ctx, prop); EXPECT_EQ(dyntype_has_property(ctx, str, "not_a_object"), -DYNTYPE_TYPEERR); EXPECT_EQ(dyntype_delete_property(ctx, str, "not_a_object"), -DYNTYPE_FALSE); EXPECT_FALSE(dyntype_is_number(ctx, str)); EXPECT_FALSE(dyntype_is_bool(ctx, str)); EXPECT_FALSE(dyntype_is_object(ctx, str)); EXPECT_FALSE(dyntype_is_undefined(ctx, str)); EXPECT_FALSE(dyntype_is_null(ctx, str)); EXPECT_TRUE(dyntype_is_string(ctx, str)); EXPECT_FALSE(dyntype_is_array(ctx, str)); EXPECT_FALSE(dyntype_is_extref(ctx, str)); str_dup = dyntype_hold(ctx, str); dyntype_release(ctx, str_dup); bool temp; double temp1; EXPECT_EQ(dyntype_to_bool(ctx, str, &temp), -DYNTYPE_TYPEERR); EXPECT_EQ(dyntype_to_number(ctx, str, &temp1), -DYNTYPE_TYPEERR); EXPECT_EQ(dyntype_to_cstring(ctx, str, &raw_value), DYNTYPE_SUCCESS); EXPECT_STREQ(raw_value, cmp_values[i]); dyntype_release(ctx, str); dyntype_free_cstring(ctx, raw_value); #if WASM_ENABLE_STRINGREF != 0 wasm_string_destroy(wasm_string); #endif } } TEST_F(TypesTest, create_array) { dyn_value_t array = dyntype_new_array(ctx, 0); dyn_value_t array_dup; EXPECT_NE(array, nullptr); EXPECT_FALSE(dyntype_is_number(ctx, array)); EXPECT_FALSE(dyntype_is_bool(ctx, array)); EXPECT_TRUE(dyntype_is_object(ctx, array)); EXPECT_FALSE(dyntype_is_undefined(ctx, array)); EXPECT_FALSE(dyntype_is_null(ctx, array)); EXPECT_FALSE(dyntype_is_string(ctx, array)); EXPECT_TRUE(dyntype_is_array(ctx, array)); EXPECT_FALSE(dyntype_is_extref(ctx, array)); array_dup = dyntype_hold(ctx, array); dyntype_release(ctx, array_dup); bool temp; double temp1; char *temp2; EXPECT_EQ(dyntype_to_bool(ctx, array, &temp), -DYNTYPE_TYPEERR); EXPECT_EQ(dyntype_to_number(ctx, array, &temp1), -DYNTYPE_TYPEERR); EXPECT_EQ(dyntype_to_cstring(ctx, array, &temp2), DYNTYPE_SUCCESS); dyntype_release(ctx, array); } // TEST_F(TypesTest, create_extern_ref) { // int data = 123; // int data2 = 42; // dyn_value_t extobj = // dyntype_new_extref(ctx, (void *)(uintptr_t)data, ExtObj, NULL); // EXPECT_NE(extobj, nullptr); // dyn_value_t prop = dyntype_new_boolean(ctx, false); // dyn_value_t prop1 = dyntype_new_boolean(ctx, false); // EXPECT_EQ(dyntype_set_property(ctx, extobj, "prop", // prop), // DYNTYPE_SUCCESS); // EXPECT_EQ(dyntype_define_property(ctx, extobj, "prop1", // prop1), // -DYNTYPE_TYPEERR); // dyn_value_t get_prop = dyntype_get_property(ctx, extobj, "prop"); // EXPECT_NE(get_prop, nullptr); // dyntype_release(ctx, get_prop); // EXPECT_EQ(dyntype_has_property(ctx, extobj, "prop"), DYNTYPE_TRUE); // EXPECT_EQ(dyntype_delete_property(ctx, extobj, "prop"), DYNTYPE_TRUE); // EXPECT_TRUE(dyntype_has_property(ctx, extobj, "@tag")); // EXPECT_TRUE(dyntype_has_property(ctx, extobj, "@ref")); // EXPECT_FALSE(dyntype_is_number(ctx, extobj)); // EXPECT_FALSE(dyntype_is_bool(ctx, extobj)); // EXPECT_FALSE(dyntype_is_undefined(ctx, extobj)); // EXPECT_FALSE(dyntype_is_null(ctx, extobj)); // EXPECT_FALSE(dyntype_is_string(ctx, extobj)); // EXPECT_FALSE(dyntype_is_array(ctx, extobj)); // EXPECT_TRUE(dyntype_is_object(ctx, extobj)); // EXPECT_TRUE(dyntype_is_extref(ctx, extobj)); // dyn_value_t extobj1 = dyntype_new_extref(ctx, (void *)(uintptr_t)data, // (external_ref_tag)(ExtArray + // 1), NULL); // EXPECT_EQ(extobj1, nullptr); // dyntype_release(ctx, extobj1); // dyn_value_t extfunc = // dyntype_new_extref(ctx, (void *)(uintptr_t)data2, ExtFunc, NULL); // EXPECT_NE(extfunc, nullptr); // EXPECT_FALSE(dyntype_is_number(ctx, extfunc)); // EXPECT_FALSE(dyntype_is_bool(ctx, extfunc)); // EXPECT_FALSE(dyntype_is_undefined(ctx, extfunc)); // EXPECT_FALSE(dyntype_is_null(ctx, extfunc)); // EXPECT_FALSE(dyntype_is_string(ctx, extfunc)); // EXPECT_FALSE(dyntype_is_array(ctx, extfunc)); // EXPECT_TRUE(dyntype_is_object(ctx, extfunc)); // EXPECT_TRUE(dyntype_is_extref(ctx, extfunc)); // void *temp_obj; // EXPECT_NE(dyntype_to_extref(ctx, extobj, &temp_obj), -DYNTYPE_TYPEERR); // EXPECT_NE(dyntype_to_extref(ctx, extfunc, &temp_obj), -DYNTYPE_TYPEERR); // void *extref_obj = nullptr; // EXPECT_EQ(dyntype_to_extref(ctx, extobj, &extref_obj), ExtObj); // EXPECT_EQ((int)(uintptr_t)extref_obj, 123); // void *extref_fun = nullptr; // EXPECT_EQ(dyntype_to_extref(ctx, extfunc, &extref_fun), ExtFunc); // EXPECT_EQ((int)(uintptr_t)extref_fun, 42); // dyntype_release(ctx, prop); // dyntype_release(ctx, prop1); // dyntype_release(ctx, extobj); // dyntype_release(ctx, extfunc); // } TEST_F(TypesTest, create_object) { dyn_value_t obj = dyntype_new_object(ctx); dyn_value_t obj_dup; EXPECT_NE(obj, nullptr); EXPECT_FALSE(dyntype_is_number(ctx, obj)); EXPECT_FALSE(dyntype_is_bool(ctx, obj)); EXPECT_TRUE(dyntype_is_object(ctx, obj)); EXPECT_FALSE(dyntype_is_undefined(ctx, obj)); EXPECT_FALSE(dyntype_is_null(ctx, obj)); EXPECT_FALSE(dyntype_is_string(ctx, obj)); EXPECT_FALSE(dyntype_is_array(ctx, obj)); EXPECT_FALSE(dyntype_is_extref(ctx, obj)); obj_dup = dyntype_hold(ctx, obj); dyntype_release(ctx, obj_dup); bool temp; double temp1; char *temp2; EXPECT_EQ(dyntype_to_bool(ctx, obj, &temp), -DYNTYPE_TYPEERR); EXPECT_EQ(dyntype_to_number(ctx, obj, &temp1), -DYNTYPE_TYPEERR); EXPECT_EQ(dyntype_to_cstring(ctx, obj, &temp2), DYNTYPE_SUCCESS); dyntype_free_cstring(ctx, temp2); /* Currently we need to manually release the object, after GC support finished, this line is not needed */ dyntype_release(ctx, obj); } TEST_F(TypesTest, create_map) { dyn_value_t obj = dyntype_new_object_with_class(ctx, "Map", 0, NULL); dyn_value_t obj1 = dyntype_new_object_with_class(ctx, "Set", 0, NULL); dyn_value_t obj_dup; EXPECT_NE(obj, nullptr); EXPECT_FALSE(dyntype_is_number(ctx, obj)); EXPECT_FALSE(dyntype_is_bool(ctx, obj)); EXPECT_TRUE(dyntype_is_object(ctx, obj)); EXPECT_FALSE(dyntype_is_undefined(ctx, obj)); EXPECT_FALSE(dyntype_is_null(ctx, obj)); EXPECT_FALSE(dyntype_is_string(ctx, obj)); EXPECT_FALSE(dyntype_is_array(ctx, obj)); EXPECT_FALSE(dyntype_is_extref(ctx, obj)); obj_dup = dyntype_hold(ctx, obj); dyntype_release(ctx, obj_dup); bool temp; double temp1; char *temp2; EXPECT_EQ(dyntype_to_bool(ctx, obj, &temp), -DYNTYPE_TYPEERR); EXPECT_EQ(dyntype_to_number(ctx, obj, &temp1), -DYNTYPE_TYPEERR); EXPECT_EQ(dyntype_to_cstring(ctx, obj, &temp2), DYNTYPE_SUCCESS); dyntype_free_cstring(ctx, temp2); /* Currently we need to manually release the object, after GC support finished, this line is not needed */ dyntype_release(ctx, obj); dyntype_release(ctx, obj1); } TEST_F(TypesTest, get_global_obj) { const char *string = "{\"a\":12, \"b\":13}"; dyn_value_t obj = dyntype_get_global(ctx, "JSON"); #if WASM_ENABLE_STRINGREF != 0 WASMString wasm_string = wasm_string_new_const(string, strlen(string)); dyn_value_t str = dyntype_new_string(ctx, wasm_string); #else dyn_value_t str = dyntype_new_string(ctx, string, strlen(string)); #endif dyn_value_t ret = NULL; dyn_value_t argv[10]; EXPECT_EQ(dyntype_has_property(ctx, obj, "stringify"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_has_property(ctx, obj, "parse"), DYNTYPE_TRUE); argv[0] = str; ret = dyntype_invoke(ctx, "parse", obj, 1, argv); EXPECT_EQ(dyntype_has_property(ctx, ret, "a"), DYNTYPE_TRUE); EXPECT_EQ(dyntype_has_property(ctx, ret, "b"), DYNTYPE_TRUE); argv[0] = ret; ret = dyntype_invoke(ctx, "stringify", obj, 1, argv); EXPECT_EQ(dyntype_is_string(ctx, ret), DYNTYPE_TRUE); char *cstr = NULL; dyntype_to_cstring(ctx, ret, &cstr); EXPECT_EQ(strcmp(cstr, "{\"a\":12,\"b\":13}"), 0); dyntype_free_cstring(ctx, cstr); dyntype_release(ctx, argv[0]); dyntype_release(ctx, ret); dyntype_release(ctx, str); dyntype_release(ctx, obj); #if WASM_ENABLE_STRINGREF != 0 wasm_string_destroy(wasm_string); #endif } ================================================ FILE: runtime-library/main.c ================================================ /* * Copyright (C) 2019 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include "bh_platform.h" #include "bh_read_file.h" #include "wasm_export.h" #include "libdyntype_export.h" extern uint32_t get_libdyntype_symbols(char **p_module_name, NativeSymbol **p_native_symbols); extern uint32_t get_lib_console_symbols(char **p_module_name, NativeSymbol **p_native_symbols); extern uint32_t get_lib_array_symbols(char **p_module_name, NativeSymbol **p_native_symbols); extern uint32_t get_lib_timer_symbols(char **p_module_name, NativeSymbol **p_native_symbols); extern uint32_t get_struct_indirect_symbols(char **p_module_name, NativeSymbol **p_native_symbols); extern dyn_value_t dyntype_callback_wasm_dispatcher(void *exec_env_v, dyn_ctx_t ctx, void *vfunc, dyn_value_t this_obj, int argc, dyn_value_t *args); #if BH_HAS_DLFCN #include #endif static int app_argc; static char **app_argv; /* clang-format off */ static int print_help() { printf("Usage: iwasm [-options] wasm_file [args...]\n"); printf("options:\n"); printf(" -f|--function name Specify a function name of the module to run rather\n" " than main\n"); #if WASM_ENABLE_LOG != 0 printf(" -v=n Set log verbose level (0 to 5, default is 2) larger\n" " level with more log\n"); #endif #if WASM_ENABLE_INTERP != 0 printf(" --interp Run the wasm app with interpreter mode\n"); #endif #if WASM_ENABLE_FAST_JIT != 0 printf(" --fast-jit Run the wasm app with fast jit mode\n"); #endif #if WASM_ENABLE_JIT != 0 printf(" --llvm-jit Run the wasm app with llvm jit mode\n"); #endif #if WASM_ENABLE_JIT != 0 && WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 printf(" --multi-tier-jit Run the wasm app with multi-tier jit mode\n"); #endif printf(" --stack-size=n Set maximum stack size in bytes, default is 64 KB\n"); printf(" --heap-size=n Set maximum heap size in bytes, default is 16 KB\n"); #if WASM_ENABLE_FAST_JIT != 0 printf(" --jit-codecache-size=n Set fast jit maximum code cache size in bytes,\n"); printf(" default is %u KB\n", FAST_JIT_DEFAULT_CODE_CACHE_SIZE / 1024); #endif #if WASM_ENABLE_GC != 0 printf(" --gc-heap-size=n Set maximum gc heap size in bytes,\n"); printf(" default is %u KB\n", GC_HEAP_SIZE_DEFAULT / 1024); #endif #if WASM_ENABLE_JIT != 0 printf(" --llvm-jit-size-level=n Set LLVM JIT size level, default is 3\n"); printf(" --llvm-jit-opt-level=n Set LLVM JIT optimization level, default is 3\n"); #endif printf(" --repl Start a very simple REPL (read-eval-print-loop) mode\n" " that runs commands in the form of \"FUNC ARG...\"\n"); #if WASM_ENABLE_LIBC_WASI != 0 printf(" --env= Pass wasi environment variables with \"key=value\"\n"); printf(" to the program, for example:\n"); printf(" --env=\"key1=value1\" --env=\"key2=value2\"\n"); printf(" --dir= Grant wasi access to the given host directories\n"); printf(" to the program, for example:\n"); printf(" --dir= --dir=\n"); printf(" --addr-pool= Grant wasi access to the given network addresses in\n"); printf(" CIRD notation to the program, seperated with ',',\n"); printf(" for example:\n"); printf(" --addr-pool=1.2.3.4/15,2.3.4.5/16\n"); printf(" --allow-resolve= Allow the lookup of the specific domain name or domain\n"); printf(" name suffixes using a wildcard, for example:\n"); printf(" --allow-resolve=example.com # allow the lookup of the specific domain\n"); printf(" --allow-resolve=*.example.com # allow the lookup of all subdomains\n"); printf(" --allow-resolve=* # allow any lookup\n"); #endif #if BH_HAS_DLFCN printf(" --native-lib= Register native libraries to the WASM module, which\n"); printf(" are shared object (.so) files, for example:\n"); printf(" --native-lib=test1.so --native-lib=test2.so\n"); #endif #if WASM_ENABLE_MULTI_MODULE != 0 printf(" --module-path= Indicate a module search path. default is current\n" " directory('./')\n"); #endif #if WASM_ENABLE_LIB_PTHREAD != 0 || WASM_ENABLE_LIB_WASI_THREADS != 0 printf(" --max-threads=n Set maximum thread number per cluster, default is 4\n"); #endif #if WASM_ENABLE_DEBUG_INTERP != 0 printf(" -g=ip:port Set the debug sever address, default is debug disabled\n"); printf(" if port is 0, then a random port will be used\n"); #endif printf(" --version Show version information\n"); return 1; } /* clang-format on */ static const void * app_instance_main(wasm_module_inst_t module_inst) { const char *exception; wasm_application_execute_main(module_inst, app_argc, app_argv); exception = wasm_runtime_get_exception(module_inst); return exception; } static const void * app_instance_func(wasm_module_inst_t module_inst, const char *func_name) { wasm_application_execute_func(module_inst, func_name, app_argc - 1, app_argv + 1); /* The result of wasm function or exception info was output inside wasm_application_execute_func(), here we don't output them again. */ return wasm_runtime_get_exception(module_inst); } /** * Split a space separated strings into an array of strings * Returns NULL on failure * Memory must be freed by caller * Based on: http://stackoverflow.com/a/11198630/471795 */ static char ** split_string(char *str, int *count) { char **res = NULL, **res1; char *p; int idx = 0; /* split string and append tokens to 'res' */ do { p = strtok(str, " "); str = NULL; res1 = res; res = (char **)realloc(res1, sizeof(char *) * (uint32)(idx + 1)); if (res == NULL) { free(res1); return NULL; } res[idx++] = p; } while (p); /** * Due to the function name, * res[0] might contain a '\' to indicate a space * func\name -> func name */ p = strchr(res[0], '\\'); while (p) { *p = ' '; p = strchr(p, '\\'); } if (count) { *count = idx - 1; } return res; } static void * app_instance_repl(wasm_module_inst_t module_inst) { char *cmd = NULL; size_t len = 0; ssize_t n; while ((printf("webassembly> "), fflush(stdout), n = getline(&cmd, &len, stdin)) != -1) { bh_assert(n > 0); if (cmd[n - 1] == '\n') { if (n == 1) continue; else cmd[n - 1] = '\0'; } if (!strcmp(cmd, "__exit__")) { printf("exit repl mode\n"); break; } app_argv = split_string(cmd, &app_argc); if (app_argv == NULL) { LOG_ERROR("Wasm prepare param failed: split string failed.\n"); break; } if (app_argc != 0) { wasm_application_execute_func(module_inst, app_argv[0], app_argc - 1, app_argv + 1); } free(app_argv); } free(cmd); return NULL; } #if WASM_ENABLE_LIBC_WASI != 0 static bool validate_env_str(char *env) { char *p = env; int key_len = 0; while (*p != '\0' && *p != '=') { key_len++; p++; } if (*p != '=' || key_len == 0) return false; return true; } #endif #if BH_HAS_DLFCN typedef uint32_t (*get_native_lib_func)(char **p_module_name, NativeSymbol **p_native_symbols); static uint32 load_and_register_native_libs(const char **native_lib_list, uint32_t native_lib_count, void **native_handle_list) { uint32_t i, native_handle_count = 0, n_native_symbols; NativeSymbol *native_symbols; char *module_name; void *handle; for (i = 0; i < native_lib_count; i++) { /* open the native library */ if (!(handle = dlopen(native_lib_list[i], RTLD_NOW | RTLD_GLOBAL)) && !(handle = dlopen(native_lib_list[i], RTLD_LAZY))) { LOG_WARNING("warning: failed to load native library %s", native_lib_list[i]); continue; } /* lookup get_native_lib func */ get_native_lib_func get_native_lib = dlsym(handle, "get_native_lib"); if (!get_native_lib) { LOG_WARNING("warning: failed to lookup `get_native_lib` function " "from native lib %s", native_lib_list[i]); dlclose(handle); continue; } n_native_symbols = get_native_lib(&module_name, &native_symbols); /* register native symbols */ if (!(n_native_symbols > 0 && module_name && native_symbols && wasm_runtime_register_natives(module_name, native_symbols, n_native_symbols))) { LOG_WARNING("warning: failed to register native lib %s", native_lib_list[i]); dlclose(handle); continue; } native_handle_list[native_handle_count++] = handle; } return native_handle_count; } static void unregister_and_unload_native_libs(uint32_t native_lib_count, void **native_handle_list) { uint32_t i, n_native_symbols; NativeSymbol *native_symbols; char *module_name; void *handle; for (i = 0; i < native_lib_count; i++) { handle = native_handle_list[i]; /* lookup get_native_lib func */ get_native_lib_func get_native_lib = dlsym(handle, "get_native_lib"); if (!get_native_lib) { LOG_WARNING("warning: failed to lookup `get_native_lib` function " "from native lib %p", handle); continue; } n_native_symbols = get_native_lib(&module_name, &native_symbols); if (n_native_symbols == 0 || module_name == NULL || native_symbols == NULL) { LOG_WARNING("warning: get_native_lib returned different values for " "native lib %p", handle); continue; } /* unregister native symbols */ if (!wasm_runtime_unregister_natives(module_name, native_symbols)) { LOG_WARNING("warning: failed to unregister native lib %p", handle); continue; } dlclose(handle); } } #endif /* BH_HAS_DLFCN */ #if WASM_ENABLE_MULTI_MODULE != 0 static char * handle_module_path(const char *module_path) { /* next character after = */ return (strchr(module_path, '=')) + 1; } static char *module_search_path = "."; static bool module_reader_callback(const char *module_name, uint8 **p_buffer, uint32_t *p_size) { const char *format = "%s/%s.wasm"; int sz = strlen(module_search_path) + strlen("/") + strlen(module_name) + strlen(".wasm") + 1; char *wasm_file_name = BH_MALLOC(sz); if (!wasm_file_name) { return false; } snprintf(wasm_file_name, sz, format, module_search_path, module_name); *p_buffer = (uint8_t *)bh_read_file_to_buffer(wasm_file_name, p_size); wasm_runtime_free(wasm_file_name); return *p_buffer != NULL; } static void moudle_destroyer(uint8 *buffer, uint32_t size) { if (!buffer) { return; } wasm_runtime_free(buffer); buffer = NULL; } #endif /* WASM_ENABLE_MULTI_MODULE */ #if WASM_ENABLE_GLOBAL_HEAP_POOL != 0 static char global_heap_buf[WASM_GLOBAL_HEAP_SIZE] = { 0 }; #endif int events_poll(wasm_exec_env_t exec_env) { /* TODO: not detect macro tasks yet */ return -1; } void execute_micro_tasks(wasm_exec_env_t exec_env, dyn_ctx_t ctx) { int err; for (;;) { /* execute the pending jobs */ for (;;) { err = dyntype_execute_pending_jobs(ctx); if (err <= 0) { if (err < 0) { dyntype_dump_error(ctx); } break; } } if (events_poll(exec_env)) break; } } int main(int argc, char *argv[]) { dyn_ctx_t dyn_ctx = NULL; int32 ret = -1; char *wasm_file = NULL; const char *func_name = NULL; uint8 *wasm_file_buf = NULL; uint32_t wasm_file_size; uint32_t stack_size = 8 * 1024, heap_size = 4 * 1024; #if WASM_ENABLE_FAST_JIT != 0 uint32_t jit_code_cache_size = FAST_JIT_DEFAULT_CODE_CACHE_SIZE; #endif #if WASM_ENABLE_GC != 0 uint32_t gc_heap_size = GC_HEAP_SIZE_DEFAULT; #endif #if WASM_ENABLE_JIT != 0 uint32_t llvm_jit_size_level = 3; uint32_t llvm_jit_opt_level = 3; #endif wasm_module_t wasm_module = NULL; wasm_module_inst_t wasm_module_inst = NULL; wasm_exec_env_t exec_env = NULL; wasm_function_inst_t start_func = NULL; RunningMode running_mode = 0; RuntimeInitArgs init_args; char error_buf[128] = { 0 }; #if WASM_ENABLE_LOG != 0 int log_verbose_level = 2; #endif bool is_repl_mode = false; bool is_xip_file = false; const char *exception = NULL; #if WASM_ENABLE_LIBC_WASI != 0 const char *dir_list[8] = { NULL }; uint32_t dir_list_size = 0; const char *env_list[8] = { NULL }; uint32_t env_list_size = 0; const char *addr_pool[8] = { NULL }; uint32_t addr_pool_size = 0; const char *ns_lookup_pool[8] = { NULL }; uint32_t ns_lookup_pool_size = 0; #endif #if BH_HAS_DLFCN const char *native_lib_list[8] = { NULL }; uint32_t native_lib_count = 0; void *native_handle_list[8] = { NULL }; uint32_t native_handle_count = 0; #endif #if WASM_ENABLE_DEBUG_INTERP != 0 char *ip_addr = NULL; int instance_port = 0; #endif /* Process options. */ for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) { if (!strcmp(argv[0], "-f") || !strcmp(argv[0], "--function")) { argc--, argv++; if (argc < 2) { return print_help(); } func_name = argv[0]; } #if WASM_ENABLE_INTERP != 0 else if (!strcmp(argv[0], "--interp")) { running_mode = Mode_Interp; } #endif #if WASM_ENABLE_FAST_JIT != 0 else if (!strcmp(argv[0], "--fast-jit")) { running_mode = Mode_Fast_JIT; } #endif #if WASM_ENABLE_JIT != 0 else if (!strcmp(argv[0], "--llvm-jit")) { running_mode = Mode_LLVM_JIT; } #endif #if WASM_ENABLE_JIT != 0 && WASM_ENABLE_FAST_JIT != 0 \ && WASM_ENABLE_LAZY_JIT != 0 else if (!strcmp(argv[0], "--multi-tier-jit")) { running_mode = Mode_Multi_Tier_JIT; } #endif #if WASM_ENABLE_LOG != 0 else if (!strncmp(argv[0], "-v=", 3)) { log_verbose_level = atoi(argv[0] + 3); if (log_verbose_level < 0 || log_verbose_level > 5) return print_help(); } #endif else if (!strcmp(argv[0], "--repl")) { is_repl_mode = true; } else if (!strncmp(argv[0], "--stack-size=", 13)) { if (argv[0][13] == '\0') return print_help(); stack_size = atoi(argv[0] + 13); } else if (!strncmp(argv[0], "--heap-size=", 12)) { if (argv[0][12] == '\0') return print_help(); heap_size = atoi(argv[0] + 12); } #if WASM_ENABLE_FAST_JIT != 0 else if (!strncmp(argv[0], "--jit-codecache-size=", 21)) { if (argv[0][21] == '\0') return print_help(); jit_code_cache_size = atoi(argv[0] + 21); } #endif #if WASM_ENABLE_GC != 0 else if (!strncmp(argv[0], "--gc-heap-size=", 15)) { if (argv[0][21] == '\0') return print_help(); gc_heap_size = atoi(argv[0] + 15); } #endif #if WASM_ENABLE_JIT != 0 else if (!strncmp(argv[0], "--llvm-jit-size-level=", 22)) { if (argv[0][22] == '\0') return print_help(); llvm_jit_size_level = atoi(argv[0] + 22); if (llvm_jit_size_level < 1) { printf("LLVM JIT size level shouldn't be smaller than 1, " "setting it to 1\n"); llvm_jit_size_level = 1; } else if (llvm_jit_size_level > 3) { printf("LLVM JIT size level shouldn't be greater than 3, " "setting it to 3\n"); llvm_jit_size_level = 3; } } else if (!strncmp(argv[0], "--llvm-jit-opt-level=", 21)) { if (argv[0][21] == '\0') return print_help(); llvm_jit_opt_level = atoi(argv[0] + 21); if (llvm_jit_opt_level < 1) { printf("LLVM JIT opt level shouldn't be smaller than 1, " "setting it to 1\n"); llvm_jit_opt_level = 1; } else if (llvm_jit_opt_level > 3) { printf("LLVM JIT opt level shouldn't be greater than 3, " "setting it to 3\n"); llvm_jit_opt_level = 3; } } #endif #if WASM_ENABLE_LIBC_WASI != 0 else if (!strncmp(argv[0], "--dir=", 6)) { if (argv[0][6] == '\0') return print_help(); if (dir_list_size >= sizeof(dir_list) / sizeof(char *)) { printf("Only allow max dir number %d\n", (int)(sizeof(dir_list) / sizeof(char *))); return 1; } dir_list[dir_list_size++] = argv[0] + 6; } else if (!strncmp(argv[0], "--env=", 6)) { char *tmp_env; if (argv[0][6] == '\0') return print_help(); if (env_list_size >= sizeof(env_list) / sizeof(char *)) { printf("Only allow max env number %d\n", (int)(sizeof(env_list) / sizeof(char *))); return 1; } tmp_env = argv[0] + 6; if (validate_env_str(tmp_env)) env_list[env_list_size++] = tmp_env; else { printf("Wasm parse env string failed: expect \"key=value\", " "got \"%s\"\n", tmp_env); return print_help(); } } /* TODO: parse the configuration file via --addr-pool-file */ else if (!strncmp(argv[0], "--addr-pool=", strlen("--addr-pool="))) { /* like: --addr-pool=100.200.244.255/30 */ char *token = NULL; if ('\0' == argv[0][12]) return print_help(); token = strtok(argv[0] + strlen("--addr-pool="), ","); while (token) { if (addr_pool_size >= sizeof(addr_pool) / sizeof(char *)) { printf("Only allow max address number %d\n", (int)(sizeof(addr_pool) / sizeof(char *))); return 1; } addr_pool[addr_pool_size++] = token; token = strtok(NULL, ";"); } } else if (!strncmp(argv[0], "--allow-resolve=", 16)) { if (argv[0][16] == '\0') return print_help(); if (ns_lookup_pool_size >= sizeof(ns_lookup_pool) / sizeof(ns_lookup_pool[0])) { printf( "Only allow max ns lookup number %d\n", (int)(sizeof(ns_lookup_pool) / sizeof(ns_lookup_pool[0]))); return 1; } ns_lookup_pool[ns_lookup_pool_size++] = argv[0] + 16; } #endif /* WASM_ENABLE_LIBC_WASI */ #if BH_HAS_DLFCN else if (!strncmp(argv[0], "--native-lib=", 13)) { if (argv[0][13] == '\0') return print_help(); if (native_lib_count >= sizeof(native_lib_list) / sizeof(char *)) { printf("Only allow max native lib number %d\n", (int)(sizeof(native_lib_list) / sizeof(char *))); return 1; } native_lib_list[native_lib_count++] = argv[0] + 13; } #endif #if WASM_ENABLE_MULTI_MODULE != 0 else if (!strncmp(argv[0], "--module-path=", strlen("--module-path="))) { module_search_path = handle_module_path(argv[0]); if (!strlen(module_search_path)) { return print_help(); } } #endif #if WASM_ENABLE_LIB_PTHREAD != 0 || WASM_ENABLE_LIB_WASI_THREADS != 0 else if (!strncmp(argv[0], "--max-threads=", 14)) { if (argv[0][14] == '\0') return print_help(); wasm_runtime_set_max_thread_num(atoi(argv[0] + 14)); } #endif #if WASM_ENABLE_DEBUG_INTERP != 0 else if (!strncmp(argv[0], "-g=", 3)) { char *port_str = strchr(argv[0] + 3, ':'); char *port_end; if (port_str == NULL) return print_help(); *port_str = '\0'; instance_port = strtoul(port_str + 1, &port_end, 10); if (port_str[1] == '\0' || *port_end != '\0') return print_help(); ip_addr = argv[0] + 3; } #endif else if (!strncmp(argv[0], "--version", 9)) { uint32_t major, minor, patch; wasm_runtime_get_version(&major, &minor, &patch); printf("iwasm %" PRIu32 ".%" PRIu32 ".%" PRIu32 "\n", major, minor, patch); return 0; } else return print_help(); } if (argc == 0) return print_help(); wasm_file = argv[0]; app_argc = argc; app_argv = argv; memset(&init_args, 0, sizeof(RuntimeInitArgs)); init_args.running_mode = running_mode; #if WASM_ENABLE_GLOBAL_HEAP_POOL != 0 init_args.mem_alloc_type = Alloc_With_Pool; init_args.mem_alloc_option.pool.heap_buf = global_heap_buf; init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); #else init_args.mem_alloc_type = Alloc_With_Allocator; init_args.mem_alloc_option.allocator.malloc_func = malloc; init_args.mem_alloc_option.allocator.realloc_func = realloc; init_args.mem_alloc_option.allocator.free_func = free; #endif #if WASM_ENABLE_FAST_JIT != 0 init_args.fast_jit_code_cache_size = jit_code_cache_size; #endif #if WASM_ENABLE_GC != 0 init_args.gc_heap_size = gc_heap_size; #endif #if WASM_ENABLE_JIT != 0 init_args.llvm_jit_size_level = llvm_jit_size_level; init_args.llvm_jit_opt_level = llvm_jit_opt_level; #endif #if WASM_ENABLE_DEBUG_INTERP != 0 init_args.instance_port = instance_port; if (ip_addr) strcpy(init_args.ip_addr, ip_addr); #endif /* initialize runtime environment */ if (!wasm_runtime_full_init(&init_args)) { printf("Init runtime environment failed.\n"); return -1; } /* initialize dyntype context and set callback dispatcher */ dyn_ctx = dyntype_context_init(); dyntype_set_callback_dispatcher(dyntype_callback_wasm_dispatcher); #if WASM_ENABLE_LOG != 0 bh_log_set_verbose_level(log_verbose_level); #endif #if BH_HAS_DLFCN native_handle_count = load_and_register_native_libs( native_lib_list, native_lib_count, native_handle_list); #endif /* Register APIs required by ts2wasm */ NativeSymbol *native_symbols; char *module_name; uint32_t symbol_count; symbol_count = get_libdyntype_symbols(&module_name, &native_symbols); if (!wasm_runtime_register_natives(module_name, native_symbols, symbol_count)) { printf("Register libdyntype APIs failed.\n"); goto fail1; } symbol_count = get_lib_console_symbols(&module_name, &native_symbols); if (!wasm_runtime_register_natives(module_name, native_symbols, symbol_count)) { printf("Register stdlib APIs failed.\n"); goto fail1; } symbol_count = get_lib_array_symbols(&module_name, &native_symbols); if (!wasm_runtime_register_natives(module_name, native_symbols, symbol_count)) { printf("Register stdlib APIs failed.\n"); goto fail1; } symbol_count = get_lib_timer_symbols(&module_name, &native_symbols); if (!wasm_runtime_register_natives(module_name, native_symbols, symbol_count)) { printf("Register stdlib APIs failed.\n"); goto fail1; } symbol_count = get_struct_indirect_symbols(&module_name, &native_symbols); if (!wasm_runtime_register_natives(module_name, native_symbols, symbol_count)) { printf("Register struct-dyn APIs failed.\n"); goto fail1; } /* load WASM byte buffer from WASM bin file */ if (!(wasm_file_buf = (uint8 *)bh_read_file_to_buffer(wasm_file, &wasm_file_size))) goto fail1; #if WASM_ENABLE_AOT != 0 if (wasm_runtime_is_xip_file(wasm_file_buf, wasm_file_size)) { uint8 *wasm_file_mapped; int map_prot = MMAP_PROT_READ | MMAP_PROT_WRITE | MMAP_PROT_EXEC; int map_flags = MMAP_MAP_32BIT; if (!(wasm_file_mapped = os_mmap(NULL, (uint32)wasm_file_size, map_prot, map_flags, os_get_invalid_handle()))) { printf("mmap memory failed\n"); wasm_runtime_free(wasm_file_buf); goto fail1; } bh_memcpy_s(wasm_file_mapped, wasm_file_size, wasm_file_buf, wasm_file_size); wasm_runtime_free(wasm_file_buf); wasm_file_buf = wasm_file_mapped; is_xip_file = true; } #endif #if WASM_ENABLE_MULTI_MODULE != 0 wasm_runtime_set_module_reader(module_reader_callback, moudle_destroyer); #endif /* load WASM module */ if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, error_buf, sizeof(error_buf)))) { printf("%s\n", error_buf); goto fail2; } #if WASM_ENABLE_LIBC_WASI != 0 wasm_runtime_set_wasi_args(wasm_module, dir_list, dir_list_size, NULL, 0, env_list, env_list_size, argv, argc); wasm_runtime_set_wasi_addr_pool(wasm_module, addr_pool, addr_pool_size); wasm_runtime_set_wasi_ns_lookup_pool(wasm_module, ns_lookup_pool, ns_lookup_pool_size); #endif /* instantiate the module */ if (!(wasm_module_inst = wasm_runtime_instantiate(wasm_module, stack_size, heap_size, error_buf, sizeof(error_buf)))) { printf("%s\n", error_buf); goto fail3; } exec_env = wasm_runtime_get_exec_env_singleton(wasm_module_inst); if (exec_env == NULL) { printf("%s\n", wasm_runtime_get_exception(wasm_module_inst)); } #if WASM_ENABLE_DEBUG_INTERP != 0 if (ip_addr != NULL) { uint32_t debug_port; debug_port = wasm_runtime_start_debug_instance(exec_env); if (debug_port == 0) { printf("Failed to start debug instance\n"); goto fail4; } } #endif ret = 0; start_func = wasm_runtime_lookup_function(wasm_module_inst, "_entry"); if (!start_func) { printf("%s\n", "Missing '_entry' function in wasm module\n"); goto fail4; } if (!wasm_runtime_call_wasm(exec_env, start_func, 0, NULL)) { printf("%s\n", wasm_runtime_get_exception(wasm_module_inst)); goto fail4; } if (is_repl_mode) { app_instance_repl(wasm_module_inst); } else if (func_name) { exception = app_instance_func(wasm_module_inst, func_name); } else { exception = app_instance_main(wasm_module_inst); } if (exception) { ret = 1; printf("%s\n", exception); } #if WASM_ENABLE_LIBC_WASI != 0 if (ret == 0) { /* propagate wasi exit code. */ ret = wasm_runtime_get_wasi_exit_code(wasm_module_inst); } #endif /* run micro tasks */ execute_micro_tasks(exec_env, dyn_ctx); fail4: /* destroy the module instance */ wasm_runtime_deinstantiate(wasm_module_inst); fail3: /* unload the module */ wasm_runtime_unload(wasm_module); fail2: /* free the file buffer */ if (!is_xip_file) { wasm_runtime_free(wasm_file_buf); } #if WASM_ENABLE_AOT != 0 else { os_munmap(wasm_file_buf, wasm_file_size); } #endif fail1: #if BH_HAS_DLFCN /* unload the native libraries */ unregister_and_unload_native_libs(native_handle_count, native_handle_list); #endif /* destroy dynamic ctx */ dyntype_context_destroy(dyn_ctx); /* destroy runtime environment */ wasm_runtime_destroy(); return ret; } ================================================ FILE: runtime-library/stdlib/lib_array.c ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #if WASM_ENABLE_STRINGREF != 0 #include "string_object.h" #endif #include "gc_export.h" #include "libdyntype_export.h" #include "object_utils.h" #include "type_utils.h" /* When growing an array, allocate more slots to avoid frequent allocation */ #define ARRAY_GROW_REDUNDANCE 16 double array_push_generic(wasm_exec_env_t exec_env, void *ctx, void *obj, void *value) { uint32_t len, value_len, capacity; wasm_array_type_t arr_type; wasm_array_obj_t new_arr; wasm_array_obj_t arr_ref = get_array_ref(obj); wasm_array_obj_t value_arr_ref = get_array_ref(value); wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); wasm_value_t init = { .gc_obj = NULL }, tmp_val = { 0 }; len = get_array_length(obj); value_len = get_array_length(value); capacity = get_array_capacity(obj); arr_type = (wasm_array_type_t)wasm_obj_get_defined_type((wasm_obj_t)arr_ref); if (value_len >= capacity - len) { /* Current array space not enough, create new array */ uint32_t new_len = len + value_len + ARRAY_GROW_REDUNDANCE; new_arr = wasm_array_obj_new_with_type(exec_env, arr_type, new_len, &init); if (!new_arr) { wasm_runtime_set_exception(module_inst, "allocate memory failed"); return 0; } wasm_array_obj_copy(new_arr, 0, arr_ref, 0, len); wasm_array_obj_copy(new_arr, len, value_arr_ref, 0, value_len); tmp_val.gc_obj = (wasm_obj_t)new_arr; wasm_struct_obj_set_field(obj, 0, &tmp_val); } else { /* Append in current array */ wasm_array_obj_copy(arr_ref, len, value_arr_ref, 0, value_len); } /* Update array length */ tmp_val.u32 = len + value_len; wasm_struct_obj_set_field(obj, 1, &tmp_val); return (double)(len + value_len); } #define ARRAY_POP_API(return_type, wasm_type, wasm_field) \ return_type array_pop_##wasm_type(wasm_exec_env_t exec_env, void *ctx, \ void *obj) \ { \ uint32_t len; \ return_type res; \ wasm_array_obj_t arr_ref = get_array_ref(obj); \ wasm_value_t value = { 0 }; \ \ len = get_array_length(obj); \ if (len == 0) { \ wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), \ "array is empty"); \ return 0; \ } \ \ wasm_array_obj_get_elem(arr_ref, len - 1, false, &value); \ res = value.wasm_field; \ \ value.u32 = len - 1; \ wasm_struct_obj_set_field(obj, 1, &value); \ \ return res; \ } ARRAY_POP_API(double, f64, f64) ARRAY_POP_API(float, f32, f32) ARRAY_POP_API(uint64, i64, i64) ARRAY_POP_API(uint32, i32, i32) ARRAY_POP_API(void *, anyref, gc_obj) /* The implementation of the basic type must first implement the to_string * interface */ #define ARRAY_JOIN_API(return_type, wasm_type, wasm_field) \ void *array_join_##wasm_type(wasm_exec_env_t exec_env, void *ctx, \ void *obj, void *separator) \ { \ wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), \ "not implemented"); \ return NULL; \ } ARRAY_JOIN_API(double, f64, f64) ARRAY_JOIN_API(float, f32, f32) ARRAY_JOIN_API(uint64, i64, i64) ARRAY_JOIN_API(uint32, i32, i32) /* string type array join interface implementation */ void * array_join_anyref(wasm_exec_env_t exec_env, void *ctx, void *obj, void *separator) { return array_to_string(exec_env, ctx, obj, separator); } /* construct new array struct with new array element, No change to the original * array */ void * array_concat_generic(wasm_exec_env_t exec_env, void *ctx, void *obj, void *value) { uint32_t len, value_len, new_length = 0; wasm_array_type_t arr_type = NULL; wasm_array_obj_t new_arr = NULL; wasm_struct_type_t struct_type = NULL; wasm_array_obj_t arr_ref = get_array_ref(obj); wasm_array_obj_t value_arr_ref = get_array_ref(value); wasm_struct_obj_t new_arr_struct = NULL; wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); wasm_value_t init = { .gc_obj = NULL }, tmp_val = { 0 }; wasm_local_obj_ref_t local_ref; bool create_new_array = false; len = get_array_length(obj); value_len = get_array_length(value); struct_type = (wasm_struct_type_t)wasm_obj_get_defined_type((wasm_obj_t)obj); arr_type = (wasm_array_type_t)wasm_obj_get_defined_type((wasm_obj_t)arr_ref); if (len == 0 && value_len != 0) { new_arr = value_arr_ref; new_length = value_len; } else if (len != 0 && value_len == 0) { new_arr = arr_ref; new_length = len; } else { new_length = len + value_len; new_arr = wasm_array_obj_new_with_type(exec_env, arr_type, new_length, &init); if (!new_arr) { wasm_runtime_set_exception(module_inst, "alloc memory failed"); return NULL; } wasm_runtime_push_local_obj_ref(exec_env, &local_ref); local_ref.val = (wasm_obj_t)new_arr; create_new_array = true; wasm_array_obj_copy(new_arr, 0, arr_ref, 0, len); wasm_array_obj_copy(new_arr, len, value_arr_ref, 0, value_len); } /* wrap with struct */ new_arr_struct = wasm_struct_obj_new_with_type(exec_env, struct_type); if (!new_arr_struct) { wasm_runtime_set_exception(module_inst, "alloc memory failed"); goto fail; } /* use new_arr_struct warp new array, and no change orginal array_struct. */ tmp_val.gc_obj = (wasm_obj_t)new_arr; wasm_struct_obj_set_field(new_arr_struct, 0, &tmp_val); tmp_val.u32 = new_length; wasm_struct_obj_set_field(new_arr_struct, 1, &tmp_val); fail: if (create_new_array) { wasm_runtime_pop_local_obj_ref(exec_env); } return new_arr_struct; } void * array_reverse_generic(wasm_exec_env_t exec_env, void *ctx, void *obj) { uint32_t i, len; wasm_value_t value1 = { 0 }, value2 = { 0 }; wasm_array_obj_t arr_ref = get_array_ref(obj); len = get_array_length(obj); if (len == 0) { return obj; } /* The first and last elements of the array are replaced sequentially */ for (i = 0; i < len / 2; i++) { wasm_array_obj_get_elem(arr_ref, i, false, &value1); wasm_array_obj_get_elem(arr_ref, len - 1 - i, false, &value2); wasm_array_obj_set_elem(arr_ref, i, &value2); wasm_array_obj_set_elem(arr_ref, len - 1 - i, &value1); } return obj; } /* Delete and return the first element of the array */ #define ARRAY_SHIFT_API(return_type, wasm_type, wasm_field) \ return_type array_shift_##wasm_type(wasm_exec_env_t exec_env, void *ctx, \ void *obj) \ { \ uint32_t len; \ return_type res; \ wasm_array_type_t arr_type; \ wasm_array_obj_t new_arr; \ wasm_array_obj_t arr_ref = get_array_ref(obj); \ wasm_module_inst_t module_inst = \ wasm_runtime_get_module_inst(exec_env); \ wasm_value_t init = { .gc_obj = NULL }, tmp_val = { 0 }, \ value = { 0 }; \ \ len = get_array_length(obj); \ arr_type = \ (wasm_array_type_t)wasm_obj_get_defined_type((wasm_obj_t)arr_ref); \ if (len == 0) { \ wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), \ "array is empty:undefined"); \ } \ wasm_array_obj_get_elem(arr_ref, 0, false, &value); \ res = value.wasm_field; \ new_arr = \ wasm_array_obj_new_with_type(exec_env, arr_type, len - 1, &init); \ if (!new_arr) { \ wasm_runtime_set_exception(module_inst, "alloc memory failed"); \ } \ wasm_array_obj_copy(new_arr, 0, arr_ref, 1, len - 1); \ tmp_val.gc_obj = (wasm_obj_t)new_arr; \ wasm_struct_obj_set_field(obj, 0, &tmp_val); \ value.u32 = len - 1; \ wasm_struct_obj_set_field(obj, 1, &value); \ return res; \ } ARRAY_SHIFT_API(double, f64, f64) ARRAY_SHIFT_API(float, f32, f32) ARRAY_SHIFT_API(uint64, i64, i64) ARRAY_SHIFT_API(uint32, i32, i32) ARRAY_SHIFT_API(void *, anyref, gc_obj) void * array_slice_generic(wasm_exec_env_t exec_env, void *ctx, void *obj, void *start_obj, void *end_obj) { uint32_t i; int32 len, new_len, start, end; wasm_struct_obj_t new_arr_struct = NULL; wasm_array_obj_t new_arr, arr_ref = get_array_ref(obj); wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); wasm_struct_type_t struct_type; wasm_array_type_t arr_type; wasm_value_t init = { 0 }, tmp_val = { 0 }; wasm_local_obj_ref_t local_ref; dyn_value_t start_idx = (dyn_value_t)wasm_anyref_obj_get_value(start_obj); dyn_value_t end_idx = (dyn_value_t)wasm_anyref_obj_get_value(end_obj); struct_type = (wasm_struct_type_t)wasm_obj_get_defined_type((wasm_obj_t)obj); arr_type = (wasm_array_type_t)wasm_obj_get_defined_type((wasm_obj_t)arr_ref); len = get_array_length(obj); start = 0; end = len; if (dyntype_is_number(dyntype_get_context(), start_idx)) { double temp; dyntype_to_number(dyntype_get_context(), start_idx, &temp); start = (int32)temp; start = start < 0 ? start + len : start; start = start < 0 ? 0 : start; } if (dyntype_is_number(dyntype_get_context(), end_idx)) { double temp; dyntype_to_number(dyntype_get_context(), end_idx, &temp); end = (int32)temp; end = end < 0 ? end + len : end; end = end < 0 ? 0 : end; end = end > len ? len : end; } new_len = end - start; new_len = new_len < 0 ? 0 : new_len; new_arr = wasm_array_obj_new_with_type(exec_env, arr_type, new_len, &init); if (!new_arr) { wasm_runtime_set_exception((wasm_module_inst_t)module_inst, "alloc memory failed"); return NULL; } wasm_runtime_push_local_obj_ref(exec_env, &local_ref); local_ref.val = (wasm_obj_t)new_arr; for (i = start; i < end; i++) { wasm_array_obj_get_elem(arr_ref, i, false, &tmp_val); wasm_array_obj_set_elem(new_arr, i - start, &tmp_val); } new_arr_struct = wasm_struct_obj_new_with_type(exec_env, struct_type); if (!new_arr_struct) { wasm_runtime_set_exception((wasm_module_inst_t)module_inst, "alloc memory failed"); goto end; } tmp_val.gc_obj = (wasm_obj_t)new_arr; wasm_struct_obj_set_field(new_arr_struct, 0, &tmp_val); tmp_val.u32 = new_len; wasm_struct_obj_set_field(new_arr_struct, 1, &tmp_val); end: wasm_runtime_pop_local_obj_ref(exec_env); return new_arr_struct; } void quick_sort(wasm_exec_env_t exec_env, wasm_array_obj_t arr, int l, int r, void *closure) { int i = l - 1, j = r + 1, pivot_idx = (l + r) >> 1; double cmp_res; wasm_value_t pivot_elem, elem, tmp_elem, left_elem, right_elem; uint32_t argv[8], argc = 0, occupied_slots = 0; uint32_t argv_bytes = sizeof(argv), pointer_slots = sizeof(void *) / sizeof(uint32); uint32_t elem_size, elem_slot; wasm_func_obj_t func = { 0 }; if (l >= r) return; GET_ELEM_FROM_CLOSURE(closure); func = (wasm_func_obj_t)func_obj.gc_obj; elem_size = 1 << wasm_array_obj_elem_size_log(arr); elem_slot = (elem_size + sizeof(uint32) - 1) / sizeof(uint32); wasm_array_obj_get_elem(arr, pivot_idx, false, &pivot_elem); /* context */ argc += pointer_slots; /* thiz */ argc += pointer_slots; /* pivot elem */ argc += elem_slot; /* elem */ argc += elem_slot; while (i < j) { do { occupied_slots = 0; i++; POPULATE_ENV_ARGS(argv, argv_bytes, occupied_slots, context, thiz); /* arg2: pivot elem */ bh_memcpy_s(argv + occupied_slots, argv_bytes - occupied_slots * sizeof(uint32), &pivot_elem.gc_obj, elem_slot * sizeof(uint32)); occupied_slots += elem_slot; /* arg3: elem*/ wasm_array_obj_get_elem(arr, i, false, &elem); bh_memcpy_s(argv + occupied_slots, argv_bytes - occupied_slots * sizeof(uint32), &elem.gc_obj, elem_slot * sizeof(uint32)); occupied_slots += elem_slot; wasm_runtime_call_func_ref(exec_env, func, argc, argv); bh_memcpy_s(&cmp_res, sizeof(cmp_res), argv, sizeof(double)); } while ((i < j) && (cmp_res > 0.0)); do { occupied_slots = 0; j--; POPULATE_ENV_ARGS(argv, argv_bytes, occupied_slots, context, thiz); /* arg2: pivot elem*/ bh_memcpy_s(argv + occupied_slots, argv_bytes - occupied_slots * sizeof(uint32), &pivot_elem.gc_obj, elem_slot * sizeof(uint32)); occupied_slots += elem_slot; /* arg3: elem*/ wasm_array_obj_get_elem(arr, j, false, &elem); bh_memcpy_s(argv + occupied_slots, argv_bytes - occupied_slots * sizeof(uint32), &elem.gc_obj, elem_slot * sizeof(uint32)); occupied_slots += elem_slot; wasm_runtime_call_func_ref(exec_env, func, argc, argv); bh_memcpy_s(&cmp_res, sizeof(cmp_res), argv, sizeof(double)); } while ((i < j) && (cmp_res < 0.0)); if (i < j) { wasm_array_obj_get_elem(arr, i, false, &left_elem); wasm_array_obj_get_elem(arr, j, false, &right_elem); tmp_elem = left_elem; wasm_array_obj_set_elem(arr, i, &right_elem); wasm_array_obj_set_elem(arr, j, &tmp_elem); } } quick_sort(exec_env, arr, l, j, closure); quick_sort(exec_env, arr, j + 1, r, closure); } void * array_sort_generic(wasm_exec_env_t exec_env, void *ctx, void *obj, void *closure) { uint32_t len; wasm_array_obj_t arr_ref = get_array_ref(obj); len = get_array_length(obj); quick_sort(exec_env, arr_ref, 0, len - 1, closure); return obj; } void * array_splice_generic(wasm_exec_env_t exec_env, void *ctx, void *obj, double start, void *delete_count_obj, void *value) { double delete_count_double; int start_idx, delete_count; uint32_t len, capacity, value_len = 0, new_len = 0; wasm_array_type_t arr_type; wasm_array_obj_t new_arr, delete_arr; wasm_array_obj_t arr_ref = get_array_ref(obj); wasm_array_obj_t value_arr_ref = NULL; wasm_struct_obj_t new_arr_struct = NULL; wasm_value_t init = { .gc_obj = NULL }, tmp_val = { 0 }; wasm_struct_type_t struct_type = (wasm_struct_type_t)wasm_obj_get_defined_type((wasm_obj_t)obj); dyn_value_t const delete_count_value = (dyn_value_t)wasm_anyref_obj_get_value(delete_count_obj); wasm_local_obj_ref_t local_ref; if (value && !dyntype_is_undefined(dyntype_get_context(), (dyn_value_t)value)) { value_arr_ref = get_array_ref(value); value_len = get_array_length(value); } len = get_array_length(obj); capacity = get_array_capacity(obj); arr_type = (wasm_array_type_t)wasm_obj_get_defined_type((wasm_obj_t)arr_ref); /* The parameter 'start' may be a decimal, convert it to int */ start_idx = (int)start; /* Ensure that start_idx keeps between 0~len*/ if (start_idx < 0) { if (-start_idx > len) { start_idx = 0; } else { start_idx += len; } } else if (start_idx >= len) { start_idx = len; } /* Ensure that delete_count keeps between 0~len */ delete_count = 0; if (dyntype_is_number(dyntype_get_context(), delete_count_value)) { dyntype_to_number(dyntype_get_context(), delete_count_value, &delete_count_double); delete_count = delete_count_double; } else if (dyntype_is_undefined(dyntype_get_context(), delete_count_value)) { delete_count = len - start; } delete_count = delete_count < 0 ? 0 : delete_count; delete_count = start_idx + delete_count > len ? len - start_idx : delete_count; delete_arr = wasm_array_obj_new_with_type(exec_env, arr_type, delete_count, &init); if (!delete_arr) { goto end1; } wasm_runtime_push_local_obj_ref(exec_env, &local_ref); local_ref.val = (wasm_obj_t)delete_arr; /* Copy deleted elements to delete_arr*/ wasm_array_obj_copy(delete_arr, 0, arr_ref, start_idx, delete_count); if (len - delete_count + value_len > capacity) { /* Current array space not enough, create new array */ new_len = len + value_len - delete_count + ARRAY_GROW_REDUNDANCE; new_arr = wasm_array_obj_new_with_type(exec_env, arr_type, new_len, &init); if (!new_arr) { goto end2; } wasm_array_obj_copy(new_arr, 0, arr_ref, 0, start_idx); wasm_array_obj_copy(new_arr, start_idx + value_len, arr_ref, start_idx + delete_count, len - delete_count - start_idx); if (value_arr_ref && value_len > 0) { wasm_array_obj_copy(new_arr, start_idx, value_arr_ref, 0, value_len); } tmp_val.gc_obj = (wasm_obj_t)new_arr; wasm_struct_obj_set_field(obj, 0, &tmp_val); } else { wasm_array_obj_copy(arr_ref, start_idx + value_len, arr_ref, start_idx + delete_count, len - delete_count - start_idx); if (value_arr_ref && value_len > 0) { wasm_array_obj_copy(arr_ref, start_idx, value_arr_ref, 0, value_len); } } /* Update the length of src array*/ tmp_val.u32 = len + value_len - delete_count; wasm_struct_obj_set_field(obj, 1, &tmp_val); /* Wrap delete_arr with struct*/ new_arr_struct = wasm_struct_obj_new_with_type(exec_env, struct_type); if (!new_arr_struct) { goto end2; } tmp_val.gc_obj = (wasm_obj_t)delete_arr; wasm_struct_obj_set_field(new_arr_struct, 0, &tmp_val); tmp_val.u32 = delete_count; wasm_struct_obj_set_field(new_arr_struct, 1, &tmp_val); wasm_runtime_pop_local_obj_ref(exec_env); return new_arr_struct; end1: wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), "alloc memory failed"); return NULL; end2: wasm_runtime_pop_local_obj_ref(exec_env); wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), "alloc memory failed"); return NULL; } /* Adds one or more elements to the beginning of the array and returns the new * length. */ double array_unshift_generic(wasm_exec_env_t exec_env, void *ctx, void *obj, void *value) { uint32_t len, value_len, capacity, new_length = 0; wasm_array_type_t arr_type; wasm_array_obj_t new_arr; wasm_array_obj_t arr_ref = get_array_ref(obj); wasm_array_obj_t value_arr_ref = get_array_ref(value); wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); wasm_value_t init = { .gc_obj = NULL }, tmp_val = { 0 }; len = get_array_length(obj); value_len = get_array_length(value); capacity = get_array_capacity(obj); arr_type = (wasm_array_type_t)wasm_obj_get_defined_type((wasm_obj_t)arr_ref); if (len == 0 && value_len != 0) { new_arr = value_arr_ref; new_length = value_len; } else if (len != 0 && value_len == 0) { new_arr = arr_ref; new_length = len; } else if (value_len >= capacity - len) { /* Current array space not enough, create new array */ uint32_t new_len = len + value_len + ARRAY_GROW_REDUNDANCE; new_arr = wasm_array_obj_new_with_type(exec_env, arr_type, new_len, &init); if (!new_arr) { wasm_runtime_set_exception(module_inst, "alloc memory failed"); return -1; } wasm_array_obj_copy(new_arr, 0, value_arr_ref, 0, value_len); wasm_array_obj_copy(new_arr, value_len, arr_ref, 0, len); new_length = len + value_len; } else { wasm_array_obj_copy(arr_ref, value_len, arr_ref, 0, len); wasm_array_obj_copy(arr_ref, 0, value_arr_ref, 0, value_len); new_arr = arr_ref; new_length = len + value_len; } tmp_val.gc_obj = (wasm_obj_t)new_arr; wasm_struct_obj_set_field(obj, 0, &tmp_val); tmp_val.u32 = new_length; wasm_struct_obj_set_field(obj, 1, &tmp_val); return new_length; } /* set from_index_obj, reduce the number of comparisons, default index o start */ #define ARRAY_INDEXOF_API(elem_type, wasm_type, wasm_field) \ double array_indexOf_##wasm_type(wasm_exec_env_t exec_env, void *ctx, \ void *obj, elem_type element, \ void *from_index_obj) \ { \ int32 len, idx = 0; \ uint32_t i; \ double idx_f = 0; \ wasm_value_t tmp_val = { 0 }; \ wasm_array_obj_t arr_ref = get_array_ref(obj); \ dyn_ctx_t dyn_ctx = dyntype_get_context(); \ len = get_array_length(obj); \ if (len == 0) { \ return -1; \ } \ if (from_index_obj) { \ dyn_value_t index_obj = \ (dyn_value_t)wasm_anyref_obj_get_value(from_index_obj); \ dyntype_to_number(dyn_ctx, index_obj, &idx_f); \ idx = (int32)idx_f; \ } \ if (idx >= len) { \ return -1; \ } \ else if (idx < -len) { \ idx = 0; \ } \ else { \ idx = idx < 0 ? (idx + len) : idx; \ } \ for (i = idx; i < len; i++) { \ wasm_array_obj_get_elem(arr_ref, i, false, &tmp_val); \ if (tmp_val.wasm_field == element) { \ return i; \ } \ } \ return -1; \ } ARRAY_INDEXOF_API(double, f64, f64) ARRAY_INDEXOF_API(float, f32, f32) ARRAY_INDEXOF_API(uint64, i64, i64) ARRAY_INDEXOF_API(uint32, i32, i32) double array_indexOf_anyref(wasm_exec_env_t exec_env, void *ctx, void *obj, void *element, void *from_index_obj) { int32 len, idx = 0; uint32_t i; double idx_f = 0; wasm_value_t tmp_val = { 0 }; wasm_array_obj_t arr_ref = get_array_ref(obj); dyn_ctx_t dyn_ctx = dyntype_get_context(); #if WASM_ENABLE_STRINGREF == 0 wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); wasm_module_t module = wasm_runtime_get_module(module_inst); #endif len = get_array_length(obj); if (len == 0) return -1; if (from_index_obj) { dyn_value_t index_obj = (dyn_value_t)wasm_anyref_obj_get_value(from_index_obj); dyntype_to_number(dyn_ctx, index_obj, &idx_f); idx = (int32)idx_f; } if (idx >= len) { return -1; } else if (idx < -len) { idx = 0; } else { idx = idx < 0 ? (idx + len) : idx; } /* loop through the array */ for (i = idx; i < len; i++) { wasm_defined_type_t value_defined_type; wasm_array_obj_get_elem(arr_ref, i, 0, &tmp_val); #if WASM_ENABLE_STRINGREF != 0 if (wasm_obj_is_stringref_obj(tmp_val.gc_obj)) { if (!wasm_obj_is_stringref_obj(element)) { return -1; } if (string_compare((wasm_stringref_obj_t)tmp_val.gc_obj, element)) { return i; } (void)value_defined_type; } #else value_defined_type = wasm_obj_get_defined_type((wasm_obj_t)tmp_val.gc_obj); if (is_ts_string_type(module, value_defined_type)) { wasm_value_t search_string = { 0 }, field1 = { 0 }; wasm_struct_obj_get_field((wasm_struct_obj_t)tmp_val.gc_obj, 1, false, &field1); wasm_array_obj_t arr2 = (wasm_array_obj_t)field1.gc_obj; uint32_t array_element_len = wasm_array_obj_length(arr2); void *array_element_ptr = wasm_array_obj_first_elem_addr(arr2); wasm_array_obj_t search_string_arr = NULL; uint32_t search_string_len = 0; void *search_string_ptr = NULL; wasm_struct_obj_get_field(element, 1, false, &search_string); search_string_arr = (wasm_array_obj_t)search_string.gc_obj; /* get search_string array len and addr */ search_string_len = wasm_array_obj_length(search_string_arr); search_string_ptr = wasm_array_obj_first_elem_addr(search_string_arr); if (search_string_len != array_element_len) { continue; } if (memcmp(search_string_ptr, array_element_ptr, array_element_len) == 0 && array_element_len == search_string_len) { return i; } } #endif /* end of WASM_ENABLE_STRINGREF != 0 */ else { if (tmp_val.gc_obj == element) return i; } } return -1; } /* array_lastIndexOf */ #define ARRAY_LAST_INDEXOF_API(elem_type, wasm_type, wasm_field) \ double array_lastIndexOf_##wasm_type(wasm_exec_env_t exec_env, void *ctx, \ void *obj, elem_type element, \ void *from_index_obj) \ { \ int32 i, len, idx = 0; \ wasm_value_t tmp_val = { 0 }; \ double idx_f = 0; \ wasm_array_obj_t arr_ref = get_array_ref(obj); \ dyn_ctx_t dyn_ctx = dyntype_get_context(); \ len = get_array_length(obj); \ if (len == 0) { \ return -1; \ } \ if (from_index_obj) { \ dyn_value_t index_obj = \ (dyn_value_t)wasm_anyref_obj_get_value(from_index_obj); \ dyntype_to_number(dyn_ctx, index_obj, &idx_f); \ idx = (int32)idx_f; \ } \ if (idx < -len) { \ return -1; \ } \ else if (idx == 0) { \ idx = len - 1; \ } \ else { \ idx = idx < 0 ? (idx + len) : (idx >= len ? (len - 1) : idx); \ } \ for (i = idx; i >= 0; i--) { \ wasm_array_obj_get_elem(arr_ref, i, false, &tmp_val); \ if (tmp_val.wasm_field == element) { \ return i; \ } \ } \ return -1; \ } ARRAY_LAST_INDEXOF_API(double, f64, f64) ARRAY_LAST_INDEXOF_API(float, f32, f32) ARRAY_LAST_INDEXOF_API(uint64, i64, i64) ARRAY_LAST_INDEXOF_API(uint32, i32, i32) double array_lastIndexOf_anyref(wasm_exec_env_t exec_env, void *ctx, void *obj, void *element, void *from_index_obj) { int32 i, len, idx = 0; double idx_f = 0; wasm_value_t tmp_val = { 0 }; wasm_array_obj_t arr_ref = get_array_ref(obj); dyn_ctx_t dyn_ctx = dyntype_get_context(); #if WASM_ENABLE_STRINGREF == 0 wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); wasm_module_t module = wasm_runtime_get_module(module_inst); #endif len = get_array_length(obj); if (len == 0) return -1; if (from_index_obj) { dyn_value_t index_obj = (dyn_value_t)wasm_anyref_obj_get_value(from_index_obj); dyntype_to_number(dyn_ctx, index_obj, &idx_f); idx = (int32)idx_f; } if (idx < -len) { return -1; } else if (idx == 0) { idx = len - 1; } else { idx = idx < 0 ? (idx + len) : (idx >= len ? (len - 1) : idx); } /* loop through the array */ for (i = idx; i >= 0; i--) { wasm_defined_type_t value_defined_type; wasm_array_obj_get_elem(arr_ref, i, 0, &tmp_val); #if WASM_ENABLE_STRINGREF != 0 if (wasm_obj_is_stringref_obj(tmp_val.gc_obj)) { if (!wasm_obj_is_stringref_obj(element)) { return -1; } if (string_compare((wasm_stringref_obj_t)tmp_val.gc_obj, element)) { return i; } (void)value_defined_type; } #else value_defined_type = wasm_obj_get_defined_type((wasm_obj_t)tmp_val.gc_obj); if (is_ts_string_type(module, value_defined_type)) { wasm_value_t field1 = { 0 }, search_string = { 0 }; wasm_struct_obj_get_field((wasm_struct_obj_t)tmp_val.gc_obj, 1, false, &field1); wasm_array_obj_t arr2 = (wasm_array_obj_t)field1.gc_obj; uint32_t array_element_len = wasm_array_obj_length(arr2); void *array_element_ptr = wasm_array_obj_first_elem_addr(arr2); wasm_struct_obj_get_field(element, 1, false, &search_string); wasm_array_obj_t search_string_arr = (wasm_array_obj_t)search_string.gc_obj; /* get search_string array len and addr */ uint32_t search_string_len = wasm_array_obj_length(search_string_arr); void *search_string_ptr = wasm_array_obj_first_elem_addr(search_string_arr); if (search_string_len != array_element_len) { continue; } if (memcmp(search_string_ptr, array_element_ptr, array_element_len) == 0 && array_element_len == search_string_len) { return i; } } #endif /* end of WASM_ENABLE_STRINGREF != 0 */ else { if (tmp_val.gc_obj == element) return i; } } return -1; } bool array_every_some_generic(wasm_exec_env_t exec_env, void *ctx, void *obj, void *closure, bool is_every) { uint32_t i, len, elem_size; bool tmp, res = false; wasm_array_obj_t arr_ref = get_array_ref(obj); len = get_array_length(obj); elem_size = get_array_element_size(arr_ref); GET_ELEM_FROM_CLOSURE(closure); /* invoke callback function */ for (i = 0; i < len; i++) { uint32_t argv[10]; uint32_t argc = 10; uint32_t occupied_slots = 0; uint32_t argv_bytes = sizeof(argv); wasm_value_t element = { 0 }; wasm_array_obj_get_elem(arr_ref, i, false, &element); /* prepare args to callback */ POPULATE_ENV_ARGS(argv, argv_bytes, occupied_slots, context, thiz); /* arg2: element */ bh_memcpy_s(argv + occupied_slots, argv_bytes - occupied_slots * sizeof(uint32), &element, elem_size); occupied_slots += elem_size / sizeof(uint32); /* arg3: index */ *(double *)(argv + occupied_slots) = i; occupied_slots += sizeof(double) / sizeof(uint32); /* arg4: arr */ bh_memcpy_s(argv + occupied_slots, argv_bytes - occupied_slots * sizeof(uint32), &obj, sizeof(void *)); wasm_runtime_call_func_ref(exec_env, (wasm_func_obj_t)func_obj.gc_obj, argc, argv); tmp = argv[0]; if (!tmp && is_every) { return false; } res = res | tmp; } return res; } bool array_every_generic(wasm_exec_env_t exec_env, void *ctx, void *obj, void *closure) { return array_every_some_generic(exec_env, ctx, obj, closure, true); } bool array_some_generic(wasm_exec_env_t exec_env, void *ctx, void *obj, void *closure) { return array_every_some_generic(exec_env, ctx, obj, closure, false); } void array_forEach_generic(wasm_exec_env_t exec_env, void *ctx, void *obj, void *closure) { uint32_t i, len, elem_size; wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); wasm_array_obj_t arr_ref = get_array_ref(obj); WASMValue element = { 0 }; dyn_ctx_t dyn_ctx; dyn_ctx = dyntype_get_context(); if (!dyn_ctx) { wasm_runtime_set_exception((wasm_module_inst_t)module_inst, "dynamic context not initialized"); return; } len = get_array_length(obj); elem_size = get_array_element_size(arr_ref); GET_ELEM_FROM_CLOSURE(closure); /* invoke callback function */ for (i = 0; i < len; i++) { uint32_t argv[10]; uint32_t argc = 10; uint32_t occupied_slots = 0; uint32_t argv_bytes = sizeof(argv); /* Must get arr ref again since it may be changed inside callback */ arr_ref = get_array_ref(obj); wasm_array_obj_get_elem(arr_ref, i, false, &element); /* prepare args to callback */ POPULATE_ENV_ARGS(argv, argv_bytes, occupied_slots, context, thiz); /* arg2: element */ bh_memcpy_s(argv + occupied_slots, argv_bytes - occupied_slots * sizeof(uint32), &element, elem_size); occupied_slots += elem_size / sizeof(uint32); /* arg3: index */ *(double *)(argv + occupied_slots) = i; occupied_slots += sizeof(double) / sizeof(uint32); /* arg4: arr */ bh_memcpy_s(argv + occupied_slots, argv_bytes - occupied_slots * sizeof(uint32), &obj, sizeof(void *)); wasm_runtime_call_func_ref(exec_env, (wasm_func_obj_t)func_obj.gc_obj, argc, argv); } } void * array_map_generic(wasm_exec_env_t exec_env, void *ctx, void *obj, void *closure) { uint32_t i, len, elem_size; uint32_t res_arr_type_idx; wasm_array_obj_t new_arr; wasm_struct_obj_t new_arr_struct = NULL; wasm_array_obj_t arr_ref = get_array_ref(obj); wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); wasm_module_t module = wasm_runtime_get_module(module_inst); wasm_value_t init = { 0 }, tmp_val = { 0 }; wasm_func_type_t cb_func_type; wasm_ref_type_t cb_ret_ref_type; wasm_local_obj_ref_t local_ref; wasm_struct_type_t res_arr_struct_type = NULL; wasm_array_type_t res_arr_type = NULL; wasm_value_t element = { 0 }; GET_ELEM_FROM_CLOSURE(closure); len = get_array_length(obj); /* get callback func return type */ cb_func_type = wasm_func_obj_get_func_type((wasm_func_obj_t)func_obj.gc_obj); cb_ret_ref_type = wasm_func_type_get_result_type(cb_func_type, 0); /* get result array type */ res_arr_type_idx = get_array_type_by_element(module, &cb_ret_ref_type, true, &res_arr_type); bh_assert( wasm_defined_type_is_array_type((wasm_defined_type_t)res_arr_type)); /* get result array struct type */ get_array_struct_type(module, res_arr_type_idx, &res_arr_struct_type); bh_assert(wasm_defined_type_is_struct_type( (wasm_defined_type_t)res_arr_struct_type)); /* create new array */ new_arr = wasm_array_obj_new_with_type(exec_env, res_arr_type, len, &init); if (!new_arr) { wasm_runtime_set_exception((wasm_module_inst_t)module_inst, "alloc memory failed"); return NULL; } wasm_runtime_push_local_obj_ref(exec_env, &local_ref); local_ref.val = (wasm_obj_t)new_arr; /* get current array element type */ elem_size = get_array_element_size(arr_ref); /* invoke callback function */ for (i = 0; i < len; i++) { uint32_t argv[10]; uint32_t argc = 10; uint32_t occupied_slots = 0; uint32_t argv_bytes = sizeof(argv); /* Must get arr ref again since it may be changed inside callback */ arr_ref = get_array_ref(obj); wasm_array_obj_get_elem(arr_ref, i, false, &element); /* prepare args to callback */ POPULATE_ENV_ARGS(argv, argv_bytes, occupied_slots, context, thiz); /* arg2: element */ bh_memcpy_s(argv + occupied_slots, argv_bytes - occupied_slots * sizeof(uint32), &element, elem_size); occupied_slots += elem_size / sizeof(uint32); /* arg3: index */ *(double *)(argv + occupied_slots) = i; occupied_slots += sizeof(double) / sizeof(uint32); /* arg4: arr */ bh_memcpy_s(argv + occupied_slots, argv_bytes - occupied_slots * sizeof(uint32), &obj, sizeof(void *)); wasm_runtime_call_func_ref(exec_env, (wasm_func_obj_t)func_obj.gc_obj, argc, argv); wasm_array_obj_set_elem(new_arr, i, (wasm_value_t *)argv); } /* wrap with struct */ new_arr_struct = wasm_struct_obj_new_with_type(exec_env, res_arr_struct_type); if (!new_arr_struct) { wasm_runtime_set_exception((wasm_module_inst_t)module_inst, "alloc memory failed"); goto end; } tmp_val.gc_obj = (wasm_obj_t)new_arr; wasm_struct_obj_set_field(new_arr_struct, 0, &tmp_val); tmp_val.u32 = len; wasm_struct_obj_set_field(new_arr_struct, 1, &tmp_val); end: wasm_runtime_pop_local_obj_ref(exec_env); return new_arr_struct; } void * array_filter_generic(wasm_exec_env_t exec_env, void *ctx, void *obj, void *closure) { uint32_t i, len, elem_size, new_arr_len, include_idx = 0; wasm_struct_obj_t new_arr_struct = NULL; wasm_array_obj_t new_arr, arr_ref = get_array_ref(obj); wasm_struct_type_t struct_type; wasm_array_type_t arr_type; wasm_local_obj_ref_t local_ref; wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); wasm_value_t init = { 0 }, tmp_val = { 0 }; wasm_obj_t *include_refs = NULL; wasm_value_t element = { 0 }; len = get_array_length(obj); struct_type = (wasm_struct_type_t)wasm_obj_get_defined_type((wasm_obj_t)obj); arr_type = (wasm_array_type_t)wasm_obj_get_defined_type((wasm_obj_t)arr_ref); elem_size = get_array_element_size(arr_ref); /* prepare a buffer to hold included reference */ include_refs = wasm_runtime_malloc(sizeof(wasm_obj_t) * len); if (!include_refs) { wasm_runtime_set_exception(module_inst, "alloc memory failed"); return NULL; } GET_ELEM_FROM_CLOSURE(closure); memset(include_refs, 0, sizeof(wasm_obj_t) * len); /* invoke callback function */ for (i = 0; i < len; i++) { uint32_t argv[10]; uint32_t argc = 10; uint32_t occupied_slots = 0; uint32_t argv_bytes = sizeof(argv); /* Must get arr ref again since it may be changed inside callback */ arr_ref = get_array_ref(obj); wasm_array_obj_get_elem(arr_ref, i, false, &element); /* prepare args to callback */ POPULATE_ENV_ARGS(argv, argv_bytes, occupied_slots, context, thiz); /* arg2: element */ bh_memcpy_s(argv + occupied_slots, argv_bytes - occupied_slots * sizeof(uint32), &element, elem_size); occupied_slots += elem_size / sizeof(uint32); /* arg3: index */ *(double *)(argv + occupied_slots) = i; occupied_slots += sizeof(double) / sizeof(uint32); /* arg4: arr */ bh_memcpy_s(argv + occupied_slots, argv_bytes - occupied_slots * sizeof(uint32), &obj, sizeof(void *)); wasm_runtime_call_func_ref(exec_env, (wasm_func_obj_t)func_obj.gc_obj, argc, argv); if (argv[0]) { include_refs[include_idx++] = element.gc_obj; } } /* create new array */ new_arr_len = include_idx; new_arr = wasm_array_obj_new_with_type(exec_env, arr_type, new_arr_len, &init); if (!new_arr) { wasm_runtime_set_exception((wasm_module_inst_t)module_inst, "alloc memory failed"); goto end1; } wasm_runtime_push_local_obj_ref(exec_env, &local_ref); local_ref.val = (wasm_obj_t)new_arr; for (i = 0; i < new_arr_len; i++) { wasm_value_t elem = { .gc_obj = include_refs[i] }; wasm_array_obj_set_elem(new_arr, i, &elem); } /* wrap with struct */ new_arr_struct = wasm_struct_obj_new_with_type(exec_env, struct_type); if (!new_arr_struct) { wasm_runtime_set_exception((wasm_module_inst_t)module_inst, "alloc memory failed"); goto end2; } tmp_val.gc_obj = (wasm_obj_t)new_arr; wasm_struct_obj_set_field(new_arr_struct, 0, &tmp_val); tmp_val.u32 = new_arr_len; wasm_struct_obj_set_field(new_arr_struct, 1, &tmp_val); end2: wasm_runtime_pop_local_obj_ref(exec_env); end1: if (include_refs) { wasm_runtime_free(include_refs); } return new_arr_struct; } #define ARRAY_REDUCE_COMMON_API(elem_type, wasm_type, wasm_field, is_right, \ underline, name) \ elem_type array_##name##underline##wasm_type( \ wasm_exec_env_t exec_env, void *ctx, void *obj, void *closure, \ elem_type initial_value) \ { \ uint32_t i, len, elem_size; \ wasm_array_obj_t arr_ref = get_array_ref(obj); \ wasm_value_t previous_value = { 0 }; \ wasm_value_t current_value = { 0 }; \ \ len = get_array_length(obj); \ if (len == 0) { \ return initial_value; \ } \ \ previous_value.wasm_field = initial_value; \ \ /* get current array element size */ \ elem_size = get_array_element_size(arr_ref); \ /* get closure context and func ref */ \ GET_ELEM_FROM_CLOSURE(closure); \ \ for (i = 0; i < len; ++i) { \ uint32_t idx = i, occupied_slots = 0; \ uint32_t argv[12]; \ uint32_t argc = 12; \ uint32_t argv_bytes = sizeof(argv); \ \ if (is_right) { \ idx = len - 1 - i; \ } \ \ wasm_array_obj_get_elem(arr_ref, idx, false, ¤t_value); \ \ /* prepare args to callback */ \ POPULATE_ENV_ARGS(argv, argv_bytes, occupied_slots, context, \ thiz); \ /* arg2: previous_value */ \ bh_memcpy_s(argv + occupied_slots, \ argv_bytes - occupied_slots * sizeof(uint32), \ &previous_value, elem_size); \ occupied_slots += elem_size / sizeof(uint32); \ /* arg3: current_value */ \ bh_memcpy_s(argv + occupied_slots, \ argv_bytes - occupied_slots * sizeof(uint32), \ ¤t_value, elem_size); \ occupied_slots += elem_size / sizeof(uint32); \ /* arg4: the index of current value */ \ *(double *)(argv + occupied_slots) = idx; \ occupied_slots += sizeof(double) / sizeof(uint32); \ /* arg5: arr */ \ bh_memcpy_s(argv + occupied_slots, \ argv_bytes - occupied_slots * sizeof(uint32), &obj, \ sizeof(void *)); \ wasm_runtime_call_func_ref( \ exec_env, (wasm_func_obj_t)func_obj.gc_obj, argc, argv); \ \ /* update accumulator */ \ bh_memcpy_s(&previous_value, sizeof(wasm_value_t), argv, \ sizeof(wasm_value_t)); \ } \ \ return previous_value.wasm_field; \ } #define ARRAY_REDUCE_API(elem_type, wasm_type, wasm_field) \ ARRAY_REDUCE_COMMON_API(elem_type, wasm_type, wasm_field, false, _, reduce) ARRAY_REDUCE_API(double, f64, f64) ARRAY_REDUCE_API(float, f32, f32) ARRAY_REDUCE_API(uint64, i64, i64) ARRAY_REDUCE_API(uint32, i32, i32) ARRAY_REDUCE_API(void *, anyref, gc_obj) #define ARRAY_REDUCE_RIGHT_API(elem_type, wasm_type, wasm_field) \ ARRAY_REDUCE_COMMON_API(elem_type, wasm_type, wasm_field, true, _, \ reduceRight) ARRAY_REDUCE_RIGHT_API(double, f64, f64) ARRAY_REDUCE_RIGHT_API(float, f32, f32) ARRAY_REDUCE_RIGHT_API(uint64, i64, i64) ARRAY_REDUCE_RIGHT_API(uint32, i32, i32) ARRAY_REDUCE_RIGHT_API(void *, anyref, gc_obj) /* Find the first elements in an array that match the conditions.*/ void * array_find_generic(wasm_exec_env_t exec_env, void *ctx, void *obj, void *closure) { uint32_t i, len, elem_size; wasm_value_t element = { 0 }; wasm_array_obj_t arr_ref = get_array_ref(obj); wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); wasm_array_type_t arr_type; dyn_ctx_t dyn_ctx; dyn_value_t found_value; dyn_ctx = dyntype_get_context(); if (!dyn_ctx) { wasm_runtime_set_exception((wasm_module_inst_t)module_inst, "dynamic context not initialized"); return NULL; } len = get_array_length(obj); arr_type = (wasm_array_type_t)wasm_obj_get_defined_type((wasm_obj_t)arr_ref); bool is_mut; wasm_ref_type_t arr_elem_ref_type = wasm_array_type_get_elem_type(arr_type, &is_mut); elem_size = get_array_element_size(arr_ref); GET_ELEM_FROM_CLOSURE(closure); /* invoke callback function */ for (i = 0; i < len; i++) { uint32_t argv[10]; uint32_t argc = 10; uint32_t occupied_slots = 0; uint32_t argv_bytes = sizeof(argv); /* Must get arr ref again since it may be changed inside callback */ arr_ref = get_array_ref(obj); wasm_array_obj_get_elem(arr_ref, i, false, &element); /* prepare args to callback */ POPULATE_ENV_ARGS(argv, argv_bytes, occupied_slots, context, thiz); /* arg2: element */ bh_memcpy_s(argv + occupied_slots, argv_bytes - occupied_slots * sizeof(uint32), &element, elem_size); occupied_slots += elem_size / sizeof(uint32); /* arg3: index */ *(double *)(argv + occupied_slots) = i; occupied_slots += sizeof(double) / sizeof(uint32); /* arg4: arr */ bh_memcpy_s(argv + occupied_slots, argv_bytes - occupied_slots * sizeof(uint32), &obj, sizeof(void *)); wasm_runtime_call_func_ref(exec_env, (wasm_func_obj_t)func_obj.gc_obj, argc, argv); if (argv[0]) { found_value = box_value_to_any(exec_env, dyn_ctx, &element, arr_elem_ref_type, false, -1); RETURN_BOX_ANYREF(found_value, dyn_ctx); break; } } RETURN_BOX_ANYREF(dyntype_new_undefined(dyn_ctx), dyn_ctx); } double array_findIndex_generic(wasm_exec_env_t exec_env, void *ctx, void *obj, void *closure) { uint32_t i, len, elem_size; wasm_array_obj_t arr_ref = get_array_ref(obj); wasm_value_t element = { 0 }; len = get_array_length(obj); elem_size = get_array_element_size(arr_ref); GET_ELEM_FROM_CLOSURE(closure); /* invoke callback function */ for (i = 0; i < len; i++) { uint32_t argv[10]; uint32_t argc = 10; uint32_t occupied_slots = 0; uint32_t argv_bytes = sizeof(argv); /* Must get arr ref again since it may be changed inside callback */ arr_ref = get_array_ref(obj); wasm_array_obj_get_elem(arr_ref, i, false, &element); /* prepare args to callback */ POPULATE_ENV_ARGS(argv, argv_bytes, occupied_slots, context, thiz); /* arg2: element */ bh_memcpy_s(argv + occupied_slots, argv_bytes - occupied_slots * sizeof(uint32), &element, elem_size); occupied_slots += elem_size / sizeof(uint32); /* arg3: index */ *(double *)(argv + occupied_slots) = i; occupied_slots += sizeof(double) / sizeof(uint32); /* arg4: arr */ bh_memcpy_s(argv + occupied_slots, argv_bytes - occupied_slots * sizeof(uint32), &obj, sizeof(void *)); wasm_runtime_call_func_ref(exec_env, (wasm_func_obj_t)func_obj.gc_obj, argc, argv); if (argv[0]) { return i; } } return -1; } #define ARRAY_FILL_API(elem_type, wasm_type, wasm_field) \ void *array_fill_##wasm_type(wasm_exec_env_t exec_env, void *ctx, \ void *obj, elem_type fill_value, \ void *start_obj, void *end_obj) \ { \ uint32_t len; \ int iter, end; \ double f_iter, f_end; \ dyn_value_t start_idx, end_idx; \ dyn_ctx_t dyn_ctx = dyntype_get_context(); \ wasm_array_obj_t arr_ref = get_array_ref(obj); \ wasm_value_t value = { 0 }; \ len = get_array_length(obj); \ if (len == 0) { \ wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), \ "array is empty"); \ return 0; \ } \ value.wasm_field = fill_value; \ start_idx = (dyn_value_t)wasm_anyref_obj_get_value(start_obj); \ end_idx = (dyn_value_t)wasm_anyref_obj_get_value(end_obj); \ dyntype_to_number(dyn_ctx, start_idx, &f_iter); \ iter = (int32)f_iter; \ dyntype_to_number(dyn_ctx, end_idx, &f_end); \ end = (int32)f_end; \ iter = iter < 0 ? 0 : iter; \ end = end > len ? len : end; \ for (; iter != end; iter++) { \ wasm_array_obj_set_elem(arr_ref, iter, &value); \ } \ return obj; \ } ARRAY_FILL_API(double, f64, f64) ARRAY_FILL_API(float, f32, f32) ARRAY_FILL_API(uint64, i64, i64) ARRAY_FILL_API(uint32, i32, i32) ARRAY_FILL_API(void *, anyref, gc_obj) /* Ensure the value of idx keeps between 0~len-1 * return -1 if idx >= len */ int compute_index(double idx, uint32_t _len) { int32 res; int32 len = _len; if (-idx <= len && idx < 0) { res = idx + len; } else if (-idx > len) { res = 0; } else if (idx >= len) { res = -1; } else { res = idx; } return res; } void * array_copyWithin_generic(wasm_exec_env_t exec_env, void *ctx, void *obj, double target, double start, void *end_obj) { int target_idx, start_idx, end_idx, copy_count; wasm_array_obj_t arr_ref = get_array_ref(obj); uint32_t len = get_array_length(obj); double end_idx_double = len; dyn_value_t const end_value = (dyn_value_t)wasm_anyref_obj_get_value(end_obj); /* Ensure that the value of target_idx keeps between 0~len-1*/ target_idx = compute_index(target, len); if (-1 == target_idx) { return obj; } /* Ensure that the value of start_idx keeps between 0~len-1*/ start_idx = compute_index(start, len); if (-1 == start_idx) { return obj; } /* If end is given, ensure that the value of end_idx keeps between 0~len*/ if (dyntype_is_number(dyntype_get_context(), end_value)) { dyntype_to_number(dyntype_get_context(), end_value, &end_idx_double); } else if (dyntype_is_undefined(dyntype_get_context(), end_value)) { end_idx_double = 0; } end_idx = compute_index(end_idx_double, len); if (-1 == end_idx) { end_idx = len; } /* Compute copy count */ copy_count = end_idx - start_idx; if (copy_count <= 0) { return obj; } copy_count = start_idx + copy_count > len ? len - start_idx : copy_count; copy_count = target_idx + copy_count > len ? len - target_idx : copy_count; /* Copy elements */ wasm_array_obj_copy(arr_ref, target_idx, arr_ref, start_idx, copy_count); return obj; } #define ARRAY_INCLUDES_API(elem_type, wasm_type, wasm_field) \ bool array_includes_##wasm_type(wasm_exec_env_t exec_env, void *ctx, \ void *obj, elem_type search_elem, \ void *from_obj) \ { \ uint32_t len = get_array_length(obj); \ elem_type element_value; \ wasm_array_obj_t arr_ref = get_array_ref(obj); \ wasm_value_t value = { 0 }; \ double from_idx_double; \ int from_idx = 0; \ dyn_value_t const from_idx_value = \ (dyn_value_t)wasm_anyref_obj_get_value(from_obj); \ \ if (dyntype_is_number(dyntype_get_context(), from_idx_value)) { \ dyntype_to_number(dyntype_get_context(), from_idx_value, \ &from_idx_double); \ from_idx = from_idx_double; \ } \ else if (dyntype_is_undefined(dyntype_get_context(), \ from_idx_value)) { \ from_idx = 0; \ } \ \ if (from_idx < 0) { \ from_idx = -from_idx > len ? 0 : from_idx + len; \ } \ \ if (len == 0 || from_idx >= len) { \ return false; \ } \ \ for (int i = from_idx; i < len; ++i) { \ wasm_array_obj_get_elem(arr_ref, i, false, &value); \ element_value = value.wasm_field; \ /* If the element type is string, use strcmp to judge if the array \ * contains search_elem */ \ if (element_value == search_elem) { \ return true; \ } \ } \ return false; \ } static bool includes_string(wasm_value_t cur_value, void *search_elem) { #if WASM_ENABLE_STRINGREF != 0 return string_compare((wasm_stringref_obj_t)cur_value.gc_obj, search_elem); #else wasm_value_t field1 = { 0 }; wasm_value_t target_string = { 0 }; uint32_t string_len, target_string_len; wasm_struct_obj_get_field((wasm_struct_obj_t)cur_value.gc_obj, 1, false, &field1); wasm_struct_obj_get_field(search_elem, 1, false, &target_string); string_len = wasm_array_obj_length((wasm_array_obj_t)field1.gc_obj); target_string_len = wasm_array_obj_length((wasm_array_obj_t)target_string.gc_obj); if (string_len != target_string_len) { return false; } void *str = wasm_array_obj_elem_addr((wasm_array_obj_t)field1.gc_obj, 0); void *str_target = wasm_array_obj_elem_addr((wasm_array_obj_t)target_string.gc_obj, 0); if (memcmp(str, str_target, string_len) == 0) { return true; } return false; #endif } bool array_includes_anyref(wasm_exec_env_t exec_env, void *ctx, void *obj, void *search_elem, void *from_obj) { int from_idx = 0; double from_idx_double; bool elem_is_string; wasm_value_t value = { 0 }; uint32_t len = get_array_length(obj); wasm_array_obj_t arr_ref = get_array_ref(obj); dyn_value_t const from_idx_value = (dyn_value_t)wasm_anyref_obj_get_value(from_obj); #if WASM_ENABLE_STRINGREF == 0 wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); wasm_module_t module = wasm_runtime_get_module(module_inst); #endif if (dyntype_is_number(dyntype_get_context(), from_idx_value)) { dyntype_to_number(dyntype_get_context(), from_idx_value, &from_idx_double); from_idx = from_idx_double; } else if (dyntype_is_undefined(dyntype_get_context(), from_idx_value)) { from_idx = 0; } if (from_idx < 0) { from_idx = -from_idx > len ? 0 : from_idx + len; } if (len == 0 || from_idx >= len) { return false; } wasm_array_obj_get_elem(arr_ref, from_idx, false, &value); #if WASM_ENABLE_STRINGREF != 0 elem_is_string = wasm_obj_is_stringref_obj(value.gc_obj); #else elem_is_string = is_ts_string_type(module, wasm_obj_get_defined_type(value.gc_obj)); #endif for (int i = from_idx; i < len; ++i) { wasm_array_obj_get_elem(arr_ref, i, 0, &value); if (elem_is_string && includes_string(value, search_elem)) { return true; } else { /* compare by address */ if (value.gc_obj == search_elem) { return true; } } } return false; } ARRAY_INCLUDES_API(double, f64, f64) ARRAY_INCLUDES_API(float, f32, f32) ARRAY_INCLUDES_API(uint64, i64, i64) ARRAY_INCLUDES_API(uint32, i32, i32) /* clang-format off */ #define REG_NATIVE_FUNC(func_name, signature) \ { #func_name, func_name, signature, NULL } static NativeSymbol native_symbols[] = { REG_NATIVE_FUNC(array_push_generic, "(rrr)F"), REG_NATIVE_FUNC(array_pop_f64, "(rr)F"), REG_NATIVE_FUNC(array_pop_f32, "(rr)f"), REG_NATIVE_FUNC(array_pop_i64, "(rr)I"), REG_NATIVE_FUNC(array_pop_i32, "(rr)i"), REG_NATIVE_FUNC(array_pop_anyref, "(rr)r"), REG_NATIVE_FUNC(array_join_f64, "(rrr)r"), REG_NATIVE_FUNC(array_join_f32, "(rrr)r"), REG_NATIVE_FUNC(array_join_i64, "(rrr)r"), REG_NATIVE_FUNC(array_join_i32, "(rrr)r"), REG_NATIVE_FUNC(array_join_anyref, "(rrr)r"), REG_NATIVE_FUNC(array_concat_generic, "(rrr)r"), REG_NATIVE_FUNC(array_reverse_generic, "(rr)r"), REG_NATIVE_FUNC(array_shift_f64, "(rr)F"), REG_NATIVE_FUNC(array_shift_f32, "(rr)f"), REG_NATIVE_FUNC(array_shift_i64, "(rr)I"), REG_NATIVE_FUNC(array_shift_i32, "(rr)i"), REG_NATIVE_FUNC(array_shift_anyref, "(rr)r"), REG_NATIVE_FUNC(array_slice_generic, "(rrrr)r"), REG_NATIVE_FUNC(array_sort_generic, "(rrr)r"), REG_NATIVE_FUNC(array_splice_generic, "(rrFrr)r"), REG_NATIVE_FUNC(array_unshift_generic, "(rrr)F"), REG_NATIVE_FUNC(array_indexOf_f64, "(rrFr)F"), REG_NATIVE_FUNC(array_indexOf_f32, "(rrfr)F"), REG_NATIVE_FUNC(array_indexOf_i64, "(rrIr)F"), REG_NATIVE_FUNC(array_indexOf_i32, "(rrir)F"), REG_NATIVE_FUNC(array_indexOf_anyref, "(rrrr)F"), REG_NATIVE_FUNC(array_lastIndexOf_f64, "(rrFr)F"), REG_NATIVE_FUNC(array_lastIndexOf_f32, "(rrfr)F"), REG_NATIVE_FUNC(array_lastIndexOf_i64, "(rrIr)F"), REG_NATIVE_FUNC(array_lastIndexOf_i32, "(rrir)F"), REG_NATIVE_FUNC(array_lastIndexOf_anyref, "(rrrr)F"), REG_NATIVE_FUNC(array_every_generic, "(rrr)i"), REG_NATIVE_FUNC(array_some_generic, "(rrr)i"), REG_NATIVE_FUNC(array_forEach_generic, "(rrr)"), REG_NATIVE_FUNC(array_map_generic, "(rrr)r"), REG_NATIVE_FUNC(array_filter_generic, "(rrr)r"), REG_NATIVE_FUNC(array_reduce_f64, "(rrrF)F"), REG_NATIVE_FUNC(array_reduce_f32, "(rrrf)f"), REG_NATIVE_FUNC(array_reduce_i64, "(rrrI)I"), REG_NATIVE_FUNC(array_reduce_i32, "(rrri)i"), REG_NATIVE_FUNC(array_reduce_anyref, "(rrrr)r"), REG_NATIVE_FUNC(array_reduceRight_f64, "(rrrF)F"), REG_NATIVE_FUNC(array_reduceRight_f32, "(rrrf)f"), REG_NATIVE_FUNC(array_reduceRight_i64, "(rrrI)I"), REG_NATIVE_FUNC(array_reduceRight_i32, "(rrri)i"), REG_NATIVE_FUNC(array_reduceRight_anyref, "(rrrr)r"), REG_NATIVE_FUNC(array_find_generic, "(rrr)r"), REG_NATIVE_FUNC(array_findIndex_generic, "(rrr)F"), REG_NATIVE_FUNC(array_fill_f64, "(rrFrr)r"), REG_NATIVE_FUNC(array_fill_f32, "(rrfrr)r"), REG_NATIVE_FUNC(array_fill_i64, "(rrIrr)r"), REG_NATIVE_FUNC(array_fill_i32, "(rrirr)r"), REG_NATIVE_FUNC(array_fill_anyref, "(rrrrr)r"), REG_NATIVE_FUNC(array_copyWithin_generic, "(rrFFr)r"), REG_NATIVE_FUNC(array_includes_f64, "(rrFr)i"), REG_NATIVE_FUNC(array_includes_f32, "(rrfr)i"), REG_NATIVE_FUNC(array_includes_i64, "(rrIr)i"), REG_NATIVE_FUNC(array_includes_i32, "(rrir)i"), REG_NATIVE_FUNC(array_includes_anyref, "(rrrr)i"), }; /* clang-format on */ uint32_t get_lib_array_symbols(char **p_module_name, NativeSymbol **p_native_symbols) { *p_module_name = "env"; *p_native_symbols = native_symbols; return sizeof(native_symbols) / sizeof(NativeSymbol); } ================================================ FILE: runtime-library/stdlib/lib_console.c ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "gc_export.h" #include "bh_platform.h" #include "libdyntype_export.h" void * Console_constructor(wasm_exec_env_t exec_env, void *obj) { return obj; } void Console_log(wasm_exec_env_t exec_env, void *thiz, void *obj) { uint32_t i, len; wasm_value_t wasm_array_data = { 0 }, wasm_array_len = { 0 }; wasm_struct_obj_t arr_struct_ref; wasm_array_obj_t arr_ref; wasm_obj_t obj_ref = (wasm_obj_t)obj; assert(wasm_obj_is_struct_obj(obj_ref)); arr_struct_ref = (wasm_struct_obj_t)obj_ref; wasm_struct_obj_get_field(arr_struct_ref, 0, false, &wasm_array_data); wasm_struct_obj_get_field(arr_struct_ref, 1, false, &wasm_array_len); arr_ref = (wasm_array_obj_t)(wasm_array_data.gc_obj); len = wasm_array_len.i32; for (i = 0; i < len; i++) { void *addr = wasm_array_obj_elem_addr(arr_ref, i); wasm_anyref_obj_t anyref = *((wasm_anyref_obj_t *)addr); dyn_value_t dynamic_val = (dyn_value_t)wasm_anyref_obj_get_value(anyref); if (dyntype_is_extref(dyntype_get_context(), dynamic_val)) { printf("[wasm object]"); } else { dyntype_dump_value(dyntype_get_context(), dynamic_val); } if (i < len - 1) { printf(" "); } } printf("\n"); } /* clang-format off */ #define REG_NATIVE_FUNC(func_name, signature) \ { #func_name, func_name, signature, NULL } static NativeSymbol native_symbols[] = { REG_NATIVE_FUNC(Console_constructor, "(r)r"), REG_NATIVE_FUNC(Console_log, "(rr)"), /* TODO */ }; /* clang-format on */ uint32_t get_lib_console_symbols(char **p_module_name, NativeSymbol **p_native_symbols) { *p_module_name = "env"; *p_native_symbols = native_symbols; return sizeof(native_symbols) / sizeof(NativeSymbol); } ================================================ FILE: runtime-library/stdlib/lib_timer.c ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "bh_hashmap.h" #include "wasm_export.h" wasm_exec_env_t env; HashMap *timer_map; void *(*_createTimer)(uint64_t timeout); bool (*_destroyTimer)(void *timer_id); double setTimeout(wasm_exec_env_t exec_env, void *closure, double delay, void *args) { void *timer_id = NULL; env = exec_env; if (_createTimer != NULL) { timer_id = _createTimer(delay); bh_hash_map_insert(timer_map, timer_id, closure); return (double)(uintptr_t)(timer_id); } return 0; } void clearTimeout(wasm_exec_env_t exec_env, double id) { void *timer_id = (void *)(uintptr_t)id; if (id == 0) { return; } if (_destroyTimer != NULL) { _destroyTimer(timer_id); } } /* clang-format off */ #define REG_NATIVE_FUNC(func_name, signature) \ { #func_name, func_name, signature, NULL } static NativeSymbol native_symbols[] = { REG_NATIVE_FUNC(setTimeout, "(rFr)F"), REG_NATIVE_FUNC(clearTimeout, "(F)"), }; /* clang-format on */ uint32_t get_lib_timer_symbols(char **p_module_name, NativeSymbol **p_native_symbols) { *p_module_name = "env"; *p_native_symbols = native_symbols; return sizeof(native_symbols) / sizeof(NativeSymbol); } ================================================ FILE: runtime-library/stringref/README.md ================================================ # stringref implementation for WAMR based on libdyntype This implementation uses dynamic typed string from libdyntype to represent string in TypeScript. ================================================ FILE: runtime-library/stringref/stringref_qjs.c ================================================ /* * Copyright (C) 2019 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "string_object.h" #include "quickjs.h" #include "dynamic-qjs/type.h" static JSValue invoke_method(JSValue obj, const char *method, int argc, JSValue *args) { DynTypeContext *dyn_ctx = dyntype_get_context(); JSContext *js_ctx = dyn_ctx->js_ctx; // JSClassCall *call_func = NULL; JSValue func = JS_GetPropertyStr(js_ctx, obj, method); JSValue ret; ret = JS_Call(js_ctx, func, obj, argc, args); JS_FreeValue(js_ctx, func); return ret; } /******************* gc finalizer *****************/ void wasm_string_destroy(WASMString str_obj) { DynTypeContext *dyn_ctx = dyntype_get_context(); JSValue js_str = JS_MKPTR(JS_TAG_STRING, (void *)str_obj); JS_FreeValue(dyn_ctx->js_ctx, js_str); } /******************* opcode functions *****************/ /* string.const */ WASMString wasm_string_new_const(const char *content, uint32 length) { DynTypeContext *dyn_ctx = dyntype_get_context(); JSValue js_str = JS_NewStringLen(dyn_ctx->js_ctx, content, length); return JS_VALUE_GET_PTR(js_str); } /* string.new_xx8 */ /* string.new_wtf16 */ /* string.new_xx8_array */ /* string.new_wtf16_array */ WASMString wasm_string_new_with_encoding(void *addr, uint32 count, EncodingFlag flag) { DynTypeContext *dyn_ctx = dyntype_get_context(); JSValue js_str = JS_NewStringLen(dyn_ctx->js_ctx, addr, count); return JS_VALUE_GET_PTR(js_str); } /* string.measure */ /* stringview_wtf16.length */ int32 wasm_string_measure(WASMString str_obj, EncodingFlag flag) { DynTypeContext *dyn_ctx = dyntype_get_context(); JSValue js_str = JS_MKPTR(JS_TAG_STRING, str_obj); JSValue length; length = JS_GetPropertyStr(dyn_ctx->js_ctx, js_str, "length"); return JS_VALUE_GET_INT(length); } /* stringview_wtf16.length */ int32 wasm_string_wtf16_get_length(WASMString str_obj) { return wasm_string_measure(str_obj, WTF16); } /* string.encode_xx8 */ /* string.encode_wtf16 */ /* stringview_wtf8.encode_xx */ /* stringview_wtf16.encode */ /* string.encode_xx8_array */ /* string.encode_wtf16_array */ int32 wasm_string_encode(WASMString str_obj, uint32 pos, uint32 count, void *addr, uint32 *next_pos, EncodingFlag flag) { DynTypeContext *dyn_ctx = dyntype_get_context(); JSValue js_str = JS_MKPTR(JS_TAG_STRING, str_obj); const char *str = NULL; size_t str_len = 0; str = JS_ToCStringLen(dyn_ctx->js_ctx, &str_len, js_str); /* If addr == NULL, just calculate the required length */ if (addr) { bh_memcpy_s(addr, str_len, str, str_len); } if (next_pos) { *next_pos = pos + count; } JS_FreeCString(dyn_ctx->js_ctx, str); return str_len; } /* string.concat */ WASMString wasm_string_concat(WASMString str_obj1, WASMString str_obj2) { JSValue js_str1 = JS_MKPTR(JS_TAG_STRING, str_obj1); JSValue js_str2 = JS_MKPTR(JS_TAG_STRING, str_obj2); JSValue js_str_res; js_str_res = invoke_method(js_str1, "concat", 1, &js_str2); return JS_VALUE_GET_PTR(js_str_res); } /* string.eq */ int32 wasm_string_eq(WASMString str_obj1, WASMString str_obj2) { JSValue js_str1 = JS_MKPTR(JS_TAG_STRING, str_obj1); JSValue js_str2 = JS_MKPTR(JS_TAG_STRING, str_obj2); JSValue res; res = invoke_method(js_str1, "localeCompare", 1, &js_str2); return JS_VALUE_GET_INT(res) == 0 ? 1 : 0; } /* string.is_usv_sequence */ int32 wasm_string_is_usv_sequence(WASMString str_obj) { return 0; } /* string.as_wtf8 */ /* string.as_wtf16 */ /* string.as_iter */ WASMString wasm_string_create_view(WASMString str_obj, StringViewType type) { DynTypeContext *dyn_ctx = dyntype_get_context(); JSValue js_str1 = JS_MKPTR(JS_TAG_STRING, str_obj); JS_DupValue(dyn_ctx->js_ctx, js_str1); return str_obj; } /* stringview_wtf8.advance */ /* stringview_iter.advance */ int32 wasm_string_advance(WASMString str_obj, uint32 pos, uint32 count, uint32 *consumed) { return 0; } /* stringview_wtf8.slice */ /* stringview_wtf16.slice */ /* stringview_iter.slice */ WASMString wasm_string_slice(WASMString str_obj, uint32 start, uint32 end, StringViewType type) { DynTypeContext *dyn_ctx = dyntype_get_context(); JSContext *js_ctx = dyn_ctx->js_ctx; JSValue js_str = JS_MKPTR(JS_TAG_STRING, str_obj); JSValue args[2] = { JS_NewFloat64(js_ctx, start), JS_NewFloat64(js_ctx, end) }; JSValue js_str_res; js_str_res = invoke_method(js_str, "slice", 2, args); return JS_VALUE_GET_PTR(js_str_res); } /* stringview_wtf16.get_codeunit */ int16 wasm_string_get_wtf16_codeunit(WASMString str_obj, int32 pos) { DynTypeContext *dyn_ctx = dyntype_get_context(); JSContext *js_ctx = dyn_ctx->js_ctx; JSValue js_str = JS_MKPTR(JS_TAG_STRING, str_obj); JSValue js_pos = JS_NewFloat64(js_ctx, pos); JSValue res; res = invoke_method(js_str, "charCodeAt", 1, &js_pos); return JS_VALUE_GET_INT(res); } /* stringview_iter.next */ uint32 wasm_string_next_codepoint(WASMString str_obj, uint32 pos) { return 0; } /* stringview_iter.rewind */ uint32 wasm_string_rewind(WASMString str_obj, uint32 pos, uint32 count, uint32 *consumed) { return 0; } /******************* application functions *****************/ void wasm_string_dump(WASMString str_obj) { DynTypeContext *dyn_ctx = dyntype_get_context(); JSContext *js_ctx = dyn_ctx->js_ctx; JSValue js_str = JS_MKPTR(JS_TAG_STRING, str_obj); const char *str; size_t len; str = JS_ToCStringLen(js_ctx, &len, js_str); fwrite(str, 1, len, stdout); JS_FreeCString(js_ctx, str); } ================================================ FILE: runtime-library/stringref/stringref_simple.c ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "string_object.h" #include "pure_dynamic.h" #include "dyn_value.h" /******************* gc finalizer *****************/ void wasm_string_destroy(WASMString str_obj) { dynamic_release(NULL, str_obj); } /******************* opcode functions *****************/ /* string.const */ WASMString wasm_string_new_const(const char *content, uint32 length) { return dyn_value_new_string(content, length); } /* string.new_xx8 */ /* string.new_wtf16 */ /* string.new_xx8_array */ /* string.new_wtf16_array */ WASMString wasm_string_new_with_encoding(void *addr, uint32 count, EncodingFlag flag) { return dyn_value_new_string(addr, count); } /* string.measure */ /* stringview_wtf16.length */ int32 wasm_string_measure(WASMString str_obj, EncodingFlag flag) { return ((DyntypeString *)str_obj)->length; } /* stringview_wtf16.length */ int32 wasm_string_wtf16_get_length(WASMString str_obj) { return wasm_string_measure(str_obj, WTF16); } /* string.encode_xx8 */ /* string.encode_wtf16 */ /* stringview_wtf8.encode_xx */ /* stringview_wtf16.encode */ /* string.encode_xx8_array */ /* string.encode_wtf16_array */ int32 wasm_string_encode(WASMString str_obj, uint32 pos, uint32 count, void *addr, uint32 *next_pos, EncodingFlag flag) { DyntypeString *dyn_str = (DyntypeString *)str_obj; uint32_t len = dyn_str->length; /* If addr == NULL, just calculate the required length */ if (addr) { bh_memcpy_s(addr, len, dyn_str->data, len); } if (next_pos) { *next_pos = pos + count; } return len; } /* string.concat */ WASMString wasm_string_concat(WASMString str_obj1, WASMString str_obj2) { return dyn_string_concat(str_obj1, str_obj2); } /* string.eq */ int32 wasm_string_eq(WASMString str_obj1, WASMString str_obj2) { return dyn_string_eq(str_obj1, str_obj2); } /* string.is_usv_sequence */ int32 wasm_string_is_usv_sequence(WASMString str_obj) { return 0; } /* string.as_wtf8 */ /* string.as_wtf16 */ /* string.as_iter */ WASMString wasm_string_create_view(WASMString str_obj, StringViewType type) { DyntypeString *dyn_str = (DyntypeString *)str_obj; dyn_str->header.ref_count++; return dyn_str; } /* stringview_wtf8.advance */ /* stringview_iter.advance */ int32 wasm_string_advance(WASMString str_obj, uint32 pos, uint32 count, uint32 *consumed) { return 0; } /* stringview_wtf8.slice */ /* stringview_wtf16.slice */ /* stringview_iter.slice */ WASMString wasm_string_slice(WASMString str_obj, uint32 start, uint32 end, StringViewType type) { return dyn_string_slice(str_obj, start, end); } /* stringview_wtf16.get_codeunit */ int16 wasm_string_get_wtf16_codeunit(WASMString str_obj, int32 pos) { DyntypeString *dyn_str = (DyntypeString *)str_obj; return dyn_str->data[pos]; } /* stringview_iter.next */ uint32 wasm_string_next_codepoint(WASMString str_obj, uint32 pos) { return 0; } /* stringview_iter.rewind */ uint32 wasm_string_rewind(WASMString str_obj, uint32 pos, uint32 count, uint32 *consumed) { return 0; } /******************* application functions *****************/ void wasm_string_dump(WASMString str_obj) { DyntypeString *dyn_str = (DyntypeString *)str_obj; fwrite(dyn_str->data, 1, dyn_str->length, stdout); } ================================================ FILE: runtime-library/struct-indirect/lib_struct_indirect.c ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "gc_type.h" static wasm_struct_obj_t check_struct_obj_type(wasm_exec_env_t exec_env, wasm_obj_t obj, int index, uint8_t type) { wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); wasm_struct_type_t struct_type; wasm_ref_type_t field_ref_type; uint8 field_type; bool is_mutable; if (!wasm_obj_is_struct_obj(obj)) { wasm_runtime_set_exception( module_inst, "can't access field of non-struct reference"); return NULL; } struct_type = (wasm_struct_type_t)wasm_obj_get_defined_type(obj); if (index < 0 || index >= wasm_struct_type_get_field_count(struct_type)) { wasm_runtime_set_exception(module_inst, "struct field index out of bounds"); return NULL; } field_ref_type = wasm_struct_type_get_field_type(struct_type, index, &is_mutable); field_type = field_ref_type.value_type; if (!((field_type == type) || (type == VALUE_TYPE_ANYREF && wasm_is_type_reftype(field_type)))) { wasm_runtime_set_exception(module_inst, "struct field type mismatch"); return NULL; } return (wasm_struct_obj_t)obj; } int struct_get_indirect_i32(wasm_exec_env_t exec_env, wasm_anyref_obj_t obj, int index) { wasm_value_t result = { 0 }; wasm_struct_obj_t struct_obj = check_struct_obj_type(exec_env, (wasm_obj_t)obj, index, VALUE_TYPE_I32); if (!struct_obj) { return 0; } wasm_struct_obj_get_field(struct_obj, index, false, &result); return result.i32; } long long struct_get_indirect_i64(wasm_exec_env_t exec_env, wasm_anyref_obj_t obj, int index) { wasm_value_t result = { 0 }; wasm_struct_obj_t struct_obj = check_struct_obj_type(exec_env, (wasm_obj_t)obj, index, VALUE_TYPE_I64); if (!struct_obj) { return 0; } wasm_struct_obj_get_field(struct_obj, index, false, &result); return result.i64; } float struct_get_indirect_f32(wasm_exec_env_t exec_env, wasm_anyref_obj_t obj, int index) { wasm_value_t result = { 0 }; wasm_struct_obj_t struct_obj = check_struct_obj_type(exec_env, (wasm_obj_t)obj, index, VALUE_TYPE_F32); if (!struct_obj) { return 0; } wasm_struct_obj_get_field(struct_obj, index, false, &result); return result.f32; } double struct_get_indirect_f64(wasm_exec_env_t exec_env, wasm_anyref_obj_t obj, int index) { wasm_value_t result = { 0 }; wasm_struct_obj_t struct_obj = check_struct_obj_type(exec_env, (wasm_obj_t)obj, index, VALUE_TYPE_F64); if (!struct_obj) { return 0; } wasm_struct_obj_get_field(struct_obj, index, false, &result); return result.f64; } void * struct_get_indirect_anyref(wasm_exec_env_t exec_env, wasm_anyref_obj_t obj, int index) { wasm_value_t result = { 0 }; wasm_struct_obj_t struct_obj = check_struct_obj_type( exec_env, (wasm_obj_t)obj, index, REF_TYPE_ANYREF); if (!struct_obj) { return NULL; } wasm_struct_obj_get_field(struct_obj, index, false, &result); return result.gc_obj; } void * struct_get_indirect_funcref(wasm_exec_env_t exec_env, wasm_anyref_obj_t obj, int index) { wasm_value_t result = { 0 }; wasm_struct_obj_t struct_obj = check_struct_obj_type( exec_env, (wasm_obj_t)obj, index, REF_TYPE_ANYREF); if (!struct_obj) { return NULL; } wasm_struct_obj_get_field(struct_obj, index, false, &result); return result.gc_obj; } void struct_set_indirect_i32(wasm_exec_env_t exec_env, wasm_anyref_obj_t obj, int index, int value) { wasm_value_t val = { .i32 = value }; wasm_struct_obj_t struct_obj = check_struct_obj_type(exec_env, (wasm_obj_t)obj, index, VALUE_TYPE_I32); if (!struct_obj) { return; } wasm_struct_obj_set_field(struct_obj, index, &val); } void struct_set_indirect_i64(wasm_exec_env_t exec_env, wasm_anyref_obj_t obj, int index, long long value) { wasm_value_t val = { .i64 = value }; wasm_struct_obj_t struct_obj = check_struct_obj_type(exec_env, (wasm_obj_t)obj, index, VALUE_TYPE_I64); if (!struct_obj) { return; } wasm_struct_obj_set_field(struct_obj, index, &val); } void struct_set_indirect_f32(wasm_exec_env_t exec_env, wasm_anyref_obj_t obj, int index, float value) { wasm_value_t val = { .f32 = value }; wasm_struct_obj_t struct_obj = check_struct_obj_type(exec_env, (wasm_obj_t)obj, index, VALUE_TYPE_F32); if (!struct_obj) { return; } wasm_struct_obj_set_field(struct_obj, index, &val); } void struct_set_indirect_f64(wasm_exec_env_t exec_env, wasm_anyref_obj_t obj, int index, double value) { wasm_value_t val = { .f64 = value }; wasm_struct_obj_t struct_obj = check_struct_obj_type(exec_env, (wasm_obj_t)obj, index, VALUE_TYPE_F64); if (!struct_obj) { return; } wasm_struct_obj_set_field(struct_obj, index, &val); } void struct_set_indirect_anyref(wasm_exec_env_t exec_env, wasm_anyref_obj_t obj, int index, void *value) { wasm_value_t val = { .gc_obj = value }; wasm_struct_obj_t struct_obj = check_struct_obj_type( exec_env, (wasm_obj_t)obj, index, REF_TYPE_ANYREF); if (!struct_obj) { return; } wasm_struct_obj_set_field(struct_obj, index, &val); } void struct_set_indirect_funcref(wasm_exec_env_t exec_env, wasm_anyref_obj_t obj, int index, void *value) { wasm_value_t val = { .gc_obj = value }; wasm_struct_obj_t struct_obj = check_struct_obj_type( exec_env, (wasm_obj_t)obj, index, REF_TYPE_ANYREF); if (!struct_obj) { return; } wasm_struct_obj_set_field(struct_obj, index, &val); } /* clang-format off */ #define REG_NATIVE_FUNC(func_name, signature) \ { #func_name, func_name, signature, NULL } static NativeSymbol native_symbols[] = { REG_NATIVE_FUNC(struct_get_indirect_i32, "(ri)i"), REG_NATIVE_FUNC(struct_get_indirect_i64, "(ri)I"), REG_NATIVE_FUNC(struct_get_indirect_f32, "(ri)f"), REG_NATIVE_FUNC(struct_get_indirect_f64, "(ri)F"), REG_NATIVE_FUNC(struct_get_indirect_anyref, "(ri)r"), REG_NATIVE_FUNC(struct_get_indirect_funcref, "(ri)r"), REG_NATIVE_FUNC(struct_set_indirect_i32, "(rii)"), REG_NATIVE_FUNC(struct_set_indirect_i64, "(riI)"), REG_NATIVE_FUNC(struct_set_indirect_f32, "(rif)"), REG_NATIVE_FUNC(struct_set_indirect_f64, "(riF)"), REG_NATIVE_FUNC(struct_set_indirect_anyref, "(rir)"), REG_NATIVE_FUNC(struct_set_indirect_funcref, "(rir)"), }; /* clang-format on */ uint32_t get_struct_indirect_symbols(char **p_module_name, NativeSymbol **p_native_symbols) { *p_module_name = "libstruct_indirect"; *p_native_symbols = native_symbols; return sizeof(native_symbols) / sizeof(NativeSymbol); } ================================================ FILE: runtime-library/struct-indirect/lib_struct_indirect.h ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "gc_export.h" int struct_get_indirect_i32(wasm_exec_env_t exec_env, wasm_anyref_obj_t obj, int index); long long struct_get_indirect_i64(wasm_exec_env_t exec_env, wasm_anyref_obj_t obj, int index); float struct_get_indirect_f32(wasm_exec_env_t exec_env, wasm_anyref_obj_t obj, int index); double struct_get_indirect_f64(wasm_exec_env_t exec_env, wasm_anyref_obj_t obj, int index); void * struct_get_indirect_anyref(wasm_exec_env_t exec_env, wasm_anyref_obj_t obj, int index); void * struct_get_indirect_funcref(wasm_exec_env_t exec_env, wasm_anyref_obj_t obj, int index); void struct_set_indirect_i32(wasm_exec_env_t exec_env, wasm_anyref_obj_t obj, int index, int value); void struct_set_indirect_i64(wasm_exec_env_t exec_env, wasm_anyref_obj_t obj, int index, long long value); void struct_set_indirect_f32(wasm_exec_env_t exec_env, wasm_anyref_obj_t obj, int index, float value); void struct_set_indirect_f64(wasm_exec_env_t exec_env, wasm_anyref_obj_t obj, int index, double value); void struct_set_indirect_anyref(wasm_exec_env_t exec_env, wasm_anyref_obj_t obj, int index, void *value); void struct_set_indirect_funcref(wasm_exec_env_t exec_env, wasm_anyref_obj_t obj, int index, void *value); ================================================ FILE: runtime-library/utils/object_utils.c ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #if WASM_ENABLE_STRINGREF != 0 #include "string_object.h" #endif #include "gc_object.h" #include "libdyntype.h" #include "object_utils.h" #include "type_utils.h" #include "wamr_utils.h" #include "libdyntype_export.h" #include "pure_dynamic.h" #include "lib_struct_indirect.h" void dynamic_object_finalizer(wasm_anyref_obj_t obj, void *data) { dyn_value_t value = (dyn_value_t)wasm_anyref_obj_get_value(obj); dyntype_release((dyn_ctx_t)data, value); } wasm_anyref_obj_t box_ptr_to_anyref(wasm_exec_env_t exec_env, dyn_ctx_t ctx, void *ptr) { wasm_anyref_obj_t any_obj = (wasm_anyref_obj_t)wasm_anyref_obj_new(exec_env, ptr); if (!any_obj) { wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), "alloc memory failed"); return NULL; } wasm_obj_set_gc_finalizer(exec_env, (wasm_obj_t)any_obj, (wasm_obj_finalizer_t)dynamic_object_finalizer, ctx); return any_obj; } static uint32 get_slot_count(wasm_ref_type_t type) { if (type.value_type == VALUE_TYPE_I32) { return sizeof(uint32) / sizeof(uint32); } else if (type.value_type == VALUE_TYPE_F64) { return sizeof(double) / sizeof(uint32); } else { return sizeof(void *) / sizeof(uint32); } } dyn_value_t box_value_to_any(wasm_exec_env_t exec_env, dyn_ctx_t ctx, wasm_value_t *value, wasm_ref_type_t type, bool is_get_property, int index) { dyn_value_t ret = NULL; wasm_defined_type_t ret_defined_type = { 0 }; wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); wasm_module_t module = wasm_runtime_get_module(module_inst); wasm_function_inst_t alloc_extref_table_slot = NULL; if (type.value_type == VALUE_TYPE_I32) { /* boolean */ uint32_t ori_value = 0; if (is_get_property) { ori_value = struct_get_indirect_i32( exec_env, (wasm_anyref_obj_t)value->gc_obj, index); } else { ori_value = value->i32; } ret = dynamic_new_boolean(ctx, (bool)ori_value); } else if (type.value_type == VALUE_TYPE_F64) { /* number */ double ori_value = 0; if (is_get_property) { ori_value = struct_get_indirect_f64( exec_env, (wasm_anyref_obj_t)value->gc_obj, index); } else { ori_value = value->f64; } ret = dynamic_new_number(ctx, ori_value); } else if (type.value_type == REF_TYPE_ANYREF) { /* any */ void *ori_value = NULL; if (is_get_property) { ori_value = struct_get_indirect_anyref( exec_env, (wasm_anyref_obj_t)value->gc_obj, index); } else { ori_value = value->gc_obj; } ret = dynamic_hold(ctx, (dyn_value_t)wasm_anyref_obj_get_value(ori_value)); } #if WASM_ENABLE_STRINGREF != 0 else if (type.value_type == REF_TYPE_STRINGREF) { /* stringref */ wasm_stringref_obj_t ori_value = NULL; if (is_get_property) { ori_value = (wasm_stringref_obj_t)struct_get_indirect_anyref( exec_env, (wasm_anyref_obj_t)value->gc_obj, index); } else { ori_value = (wasm_stringref_obj_t)value->gc_obj; } ret = dyntype_new_string( ctx, (void *)wasm_stringref_obj_get_value(ori_value)); } #endif else { void *ori_value = NULL; wasm_struct_type_t new_closure_type = NULL; ret_defined_type = wasm_get_defined_type(module, type.heap_type); wasm_value_t tmp_func = { 0 }; if (is_get_property) { /* if property is a method, then we can only get funcref, need to * wrap to closure manually */ if (wasm_defined_type_is_func_type(ret_defined_type)) { void *vtable = struct_get_indirect_anyref( exec_env, (wasm_anyref_obj_t)value->gc_obj, 0); void *func_ref = struct_get_indirect_funcref( exec_env, (wasm_anyref_obj_t)vtable, index); get_closure_struct_type(module, &new_closure_type); ret_defined_type = (wasm_defined_type_t)new_closure_type; ori_value = wasm_struct_obj_new_with_type(exec_env, new_closure_type); tmp_func.gc_obj = (wasm_obj_t)func_ref; wasm_struct_obj_set_field(ori_value, THIZ_INDEX, value); wasm_struct_obj_set_field(ori_value, FUNC_INDEX, &tmp_func); } else { ori_value = struct_get_indirect_anyref( exec_env, (wasm_anyref_obj_t)value->gc_obj, index); } } else { bh_memcpy_s(&ori_value, sizeof(void *), value, sizeof(void *)); } if (wasm_defined_type_is_struct_type(ret_defined_type)) { #if WASM_ENABLE_STRINGREF == 0 if (is_ts_string_type(module, ret_defined_type)) { const char *str = get_str_from_string_struct(ori_value); uint32_t str_len = get_str_length_from_string_struct(ori_value); ret = dynamic_new_string(ctx, str, str_len); } else { #endif wasm_value_t wasm_ret_value = { 0 }; uint32_t occupied_slots = 0; uint32_t extref_argv[6] = { 0 }; uint32_t extref_agc = 6; int tag = 0; if (is_ts_array_type(module, ret_defined_type)) { tag = ExtArray; } else if (is_ts_closure_type(module, ret_defined_type)) { tag = ExtFunc; } else { tag = ExtObj; } alloc_extref_table_slot = wasm_runtime_lookup_function( module_inst, "allocExtRefTableSlot"); bh_assert(alloc_extref_table_slot); bh_memcpy_s(extref_argv + occupied_slots, sizeof(wasm_anyref_obj_t), &ori_value, sizeof(wasm_anyref_obj_t)); occupied_slots += sizeof(wasm_anyref_obj_t) / sizeof(uint32); extref_agc = occupied_slots; if (!wasm_runtime_call_wasm(exec_env, alloc_extref_table_slot, extref_agc, extref_argv)) { return NULL; } bh_memcpy_s(&wasm_ret_value, sizeof(wasm_anyref_obj_t), extref_argv, sizeof(wasm_anyref_obj_t)); ret = dyntype_new_extref( ctx, (void *)(uintptr_t)wasm_ret_value.i32, tag, NULL); #if WASM_ENABLE_STRINGREF == 0 } #endif } } return ret; } #if WASM_ENABLE_STRINGREF != 0 bool string_compare(wasm_stringref_obj_t lhs, wasm_stringref_obj_t rhs) { return wasm_string_eq((WASMString)wasm_stringref_obj_get_value(lhs), (WASMString)wasm_stringref_obj_get_value(rhs)); } #endif /* end of WASM_ENABLE_STRINGREF != 0 */ #if WASM_ENABLE_STRINGREF != 0 wasm_stringref_obj_t #else wasm_struct_obj_t #endif unbox_string_from_any(wasm_exec_env_t exec_env, dyn_ctx_t ctx, dyn_value_t obj) { char *value = NULL; int ret; void *new_string_struct = NULL; ret = dynamic_to_cstring(ctx, obj, &value); if (ret != DYNTYPE_SUCCESS) { goto end; } new_string_struct = create_wasm_string(exec_env, value); if (!new_string_struct) { goto end; } end: if (value) { dyntype_free_cstring(UNBOX_ANYREF(ctx), value); } return new_string_struct; } void unbox_value_from_any(wasm_exec_env_t exec_env, dyn_ctx_t ctx, dyn_value_t obj, wasm_ref_type_t type, wasm_value_t *unboxed_value, bool is_set_property, int index) { wasm_defined_type_t ret_defined_type = { 0 }; wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); wasm_module_t module = wasm_runtime_get_module(module_inst); if (type.value_type == VALUE_TYPE_I32) { bool ret_value; if (dynamic_to_bool(ctx, obj, &ret_value) != DYNTYPE_SUCCESS) { goto fail; } if (is_set_property) { struct_set_indirect_i32(exec_env, (wasm_anyref_obj_t)unboxed_value->gc_obj, index, ret_value); } else { unboxed_value->i32 = ret_value; } } else if (type.value_type == VALUE_TYPE_F64) { double ret_value; if (dyntype_to_number(ctx, obj, &ret_value) != DYNTYPE_SUCCESS) { goto fail; } if (is_set_property) { struct_set_indirect_f64(exec_env, (wasm_anyref_obj_t)unboxed_value->gc_obj, index, ret_value); } else { unboxed_value->f64 = ret_value; } } else if (type.value_type == REF_TYPE_ANYREF) { void *ret_value = box_ptr_to_anyref(exec_env, ctx, dyntype_hold(ctx, obj)); if (is_set_property) { struct_set_indirect_anyref(exec_env, (wasm_anyref_obj_t)unboxed_value->gc_obj, index, ret_value); } else { unboxed_value->gc_obj = ret_value; } } #if WASM_ENABLE_STRINGREF != 0 else if (type.value_type == REF_TYPE_STRINGREF) { /* stringref */ void *str_obj = dyntype_to_string(ctx, (obj)); wasm_stringref_obj_t ret_value = wasm_stringref_obj_new(exec_env, str_obj); if (is_set_property) { struct_set_indirect_anyref(exec_env, (wasm_anyref_obj_t)unboxed_value->gc_obj, index, ret_value); } else { unboxed_value->gc_obj = (wasm_obj_t)ret_value; } } #endif else { ret_defined_type = wasm_get_defined_type(module, type.heap_type); if (wasm_defined_type_is_struct_type(ret_defined_type)) { #if WASM_ENABLE_STRINGREF == 0 if (is_ts_string_type(module, ret_defined_type)) { void *ret_value = unbox_string_from_any(exec_env, ctx, obj); if (is_set_property) { struct_set_indirect_anyref( exec_env, (wasm_anyref_obj_t)unboxed_value->gc_obj, index, ret_value); } else { unboxed_value->gc_obj = ret_value; } } else { #endif void *ret_value; uint32_t table_idx; void *p_table_index; int32_t tag = dynamic_to_extref(ctx, obj, &p_table_index); if (tag == -DYNTYPE_TYPEERR) { goto fail; } table_idx = (uint32_t)(uintptr_t)p_table_index; ret_value = wamr_utils_get_table_element(exec_env, table_idx); if (is_set_property) { struct_set_indirect_anyref( exec_env, (wasm_anyref_obj_t)unboxed_value->gc_obj, index, ret_value); } else { unboxed_value->gc_obj = ret_value; } #if WASM_ENABLE_STRINGREF == 0 } #endif } } return; fail: wasm_runtime_set_exception(module_inst, "failed to unbox value from any"); } dyn_value_t call_wasm_func_with_boxing(wasm_exec_env_t exec_env, dyn_ctx_t ctx, wasm_anyref_obj_t func_any_obj, uint32_t argc, dyn_value_t *func_args) { int i; dyn_value_t ret = NULL; wasm_func_obj_t func_ref = { 0 }; wasm_func_type_t func_type = { 0 }; wasm_ref_type_t result_type = { 0 }; wasm_ref_type_t tmp_param_type = { 0 }; wasm_struct_obj_t closure_obj = { 0 }; wasm_value_t tmp_result; wasm_value_t tmp_param; wasm_local_obj_ref_t *local_refs = NULL; uint32_t slot_count = 0, local_ref_count = 0; uint32_t occupied_slots = 0; uint32_t *argv = NULL; uint32_t bsize = 0; uint32_t result_count = 0; uint32_t param_count = 0; bool is_success; closure_obj = (wasm_struct_obj_t)func_any_obj; GET_ELEM_FROM_CLOSURE(closure_obj); func_ref = (wasm_func_obj_t)(func_obj.gc_obj); func_type = wasm_func_obj_get_func_type(func_ref); result_count = wasm_func_type_get_result_count(func_type); param_count = wasm_func_type_get_param_count(func_type); if (param_count != argc + ENV_PARAM_LEN) { const char *exception = "libdyntype: function param count not equal with the real param"; #if WASM_ENABLE_STRINGREF != 0 return dyntype_throw_exception( ctx, dyntype_new_string( ctx, wasm_stringref_obj_get_value( create_wasm_string(exec_env, exception)))); #else return dyntype_throw_exception( ctx, dyntype_new_string(ctx, exception, strlen(exception))); #endif } bsize = sizeof(uint64) * (param_count); argv = wasm_runtime_malloc(bsize); if (!argv) { const char *exception = "libdyntype: alloc memory failed"; #if WASM_ENABLE_STRINGREF != 0 return dyntype_throw_exception( ctx, dyntype_new_string( ctx, wasm_stringref_obj_get_value( create_wasm_string(exec_env, exception)))); #else return dyntype_throw_exception( ctx, dyntype_new_string(ctx, exception, strlen(exception))); #endif } /* reserve space for context and thiz */ POPULATE_ENV_ARGS(argv, bsize, occupied_slots, context, thiz); if (argc > 0 && !(local_refs = wasm_runtime_malloc(sizeof(wasm_local_obj_ref_t) * argc))) { const char *exception = "libdyntype: alloc memory failed"; #if WASM_ENABLE_STRINGREF != 0 ret = dyntype_throw_exception( ctx, dyntype_new_string( ctx, wasm_stringref_obj_get_value( create_wasm_string(exec_env, exception)))); #else ret = dyntype_throw_exception( ctx, dyntype_new_string(ctx, exception, strlen(exception))); #endif goto end; } for (i = 0; i < argc; i++) { tmp_param_type = wasm_func_type_get_param_type(func_type, i + ENV_PARAM_LEN); slot_count = get_slot_count(tmp_param_type); unbox_value_from_any(exec_env, ctx, func_args[i], tmp_param_type, &tmp_param, false, -1); if (tmp_param_type.value_type == VALUE_TYPE_ANYREF #if WASM_ENABLE_STRINGREF != 0 || tmp_param_type.value_type == VALUE_TYPE_STRINGREF #endif ) { /* unbox_value_from_any will create anyref for any-objects, we must * hold its reference to avoid it being claimed */ wasm_runtime_push_local_obj_ref(exec_env, &local_refs[local_ref_count]); local_refs[local_ref_count++].val = tmp_param.gc_obj; } bh_memcpy_s(argv + occupied_slots, bsize - occupied_slots * sizeof(uint32), &tmp_param, slot_count * sizeof(uint32)); occupied_slots += slot_count; } if (local_ref_count) { wasm_runtime_pop_local_obj_refs(exec_env, local_ref_count); } is_success = wasm_runtime_call_func_ref(exec_env, func_ref, occupied_slots, argv); if (!is_success) { /* static throw or dynamic throw can not be defined in compilation */ /* workaround: exception-handling proposal is not implemented in * WAMR yet, so where to get the thrown exception in unkown, just * pass undefined as exception */ ret = dyntype_throw_exception(ctx, dyntype_new_undefined(ctx)); goto end; } if (result_count > 0) { result_type = wasm_func_type_get_result_type(func_type, 0); slot_count = get_slot_count(result_type); bh_memcpy_s(&tmp_result, slot_count * sizeof(uint32), argv, slot_count * sizeof(uint32)); ret = box_value_to_any(exec_env, ctx, &tmp_result, result_type, false, -1); } else { ret = dynamic_new_undefined(ctx); } end: if (local_refs) { wasm_runtime_free(local_refs); } wasm_runtime_free(argv); return ret; } ================================================ FILE: runtime-library/utils/object_utils.h ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #ifndef __OBJECT_UTILS_H_ #define __OBJECT_UTILS_H_ #include "gc_export.h" #include "libdyntype.h" wasm_anyref_obj_t box_ptr_to_anyref(wasm_exec_env_t exec_env, dyn_ctx_t ctx, void *ptr); dyn_value_t box_value_to_any(wasm_exec_env_t exec_env, dyn_ctx_t ctx, wasm_value_t *value, wasm_ref_type_t type, bool is_get_property, int index); void unbox_value_from_any(wasm_exec_env_t exec_env, dyn_ctx_t ctx, void *obj, wasm_ref_type_t type, wasm_value_t *unboxed_value, bool is_set_property, int index); dyn_value_t call_wasm_func_with_boxing(wasm_exec_env_t exec_env, dyn_ctx_t ctx, wasm_anyref_obj_t func_any_obj, uint32_t argc, dyn_value_t *func_args); #if WASM_ENABLE_STRINGREF != 0 bool string_compare(wasm_stringref_obj_t lhs, wasm_stringref_obj_t rhs); #endif #endif /* end of __OBJECT_UTILS_H_ */ ================================================ FILE: runtime-library/utils/type_utils.c ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #if WASM_ENABLE_STRINGREF != 0 #include "string_object.h" #endif #include "type_utils.h" #include "gc_export.h" #include "gc_object.h" #include "libdyntype.h" #include "wamr_utils.h" #include "libdyntype_export.h" #define OFFSET_OF_TYPE_ID 0 #define OFFSET_OF_IMPL_ID 4 #define OFFSET_OF_COUNT 8 #define OFFSET_OF_META_FIELDS 12 #define SIZEOF_META_FIELD 12 #define META_FLAG_MASK 0x0000000F #define META_INDEX_MASK 0xFFFFFFF0 #define OFFSET_OF_FIELD_FLAG_AND_INDEX 4 #define OFFSET_OF_FIELD_TYPE 8 /** start type id of custom type */ #define CUSTOM_TYPE_BEGIN 1052 /* utilities for closure object * closure struct (WasmGC struct) +----------+ +---------------------------+ | 0:context|----->| context | +----------+ +---------------------------+ | 1:thiz |----->| thiz | +----------+ +---------------------------+ | 2:func | | func | +----------+ +---------------------------+ */ bool is_ts_closure_type(wasm_module_t wasm_module, wasm_defined_type_t type) { bool is_struct_type; wasm_struct_type_t struct_type; uint32_t field_count; bool mut; wasm_ref_type_t field_type; uint32_t field_type_idx = 0; wasm_defined_type_t field_defined_type; is_struct_type = wasm_defined_type_is_struct_type(type); if (!is_struct_type) { return false; } struct_type = (wasm_struct_type_t)type; field_count = wasm_struct_type_get_field_count(struct_type); if (field_count != ENV_PARAM_LEN + 1) { return false; } field_type = wasm_struct_type_get_field_type(struct_type, CONTEXT_INDEX, &mut); field_type_idx = field_type.heap_type; field_defined_type = wasm_get_defined_type(wasm_module, field_type_idx); if (!wasm_defined_type_is_struct_type(field_defined_type)) { return false; } field_type = wasm_struct_type_get_field_type(struct_type, THIZ_INDEX, &mut); field_type_idx = field_type.heap_type; field_defined_type = wasm_get_defined_type(wasm_module, field_type_idx); if (!wasm_defined_type_is_struct_type(field_defined_type)) { return false; } field_type = wasm_struct_type_get_field_type(struct_type, FUNC_INDEX, &mut); field_type_idx = field_type.heap_type; field_defined_type = wasm_get_defined_type(wasm_module, field_type_idx); if (!wasm_defined_type_is_func_type(field_defined_type)) { return false; } return true; } /* utilities for array object * array struct (WasmGC struct) +----------+ +---------------------------+ | 0:data |----->| content (WasmGC array) | +----------+ +---------------------------+ | 1:size | ^ ^ +----------+ |<------- capacity ------>| */ bool is_ts_array_type(wasm_module_t wasm_module, wasm_defined_type_t type) { bool is_struct_type; wasm_struct_type_t struct_type; uint32_t field_count; bool mut; wasm_ref_type_t field_type; uint32_t array_type_idx = 0; wasm_defined_type_t array_type; is_struct_type = wasm_defined_type_is_struct_type(type); if (!is_struct_type) { return false; } struct_type = (wasm_struct_type_t)type; field_count = wasm_struct_type_get_field_count(struct_type); if (field_count != 2) { return false; } field_type = wasm_struct_type_get_field_type(struct_type, 1, &mut); if (field_type.value_type != VALUE_TYPE_I32 || !mut) { return false; } field_type = wasm_struct_type_get_field_type(struct_type, 0, &mut); array_type_idx = field_type.heap_type; array_type = wasm_get_defined_type(wasm_module, array_type_idx); if (!mut || !wasm_defined_type_is_array_type(array_type)) { return false; } return true; } uint32_t get_array_length(wasm_struct_obj_t obj) { wasm_value_t wasm_array_len = { 0 }; bh_assert(wasm_obj_is_struct_obj((wasm_obj_t)obj)); wasm_struct_obj_get_field(obj, 1, false, &wasm_array_len); return wasm_array_len.u32; } wasm_array_obj_t get_array_ref(wasm_struct_obj_t obj) { wasm_value_t wasm_array = { 0 }; bh_assert(wasm_obj_is_struct_obj((wasm_obj_t)obj)); wasm_struct_obj_get_field(obj, 0, false, &wasm_array); return (wasm_array_obj_t)wasm_array.gc_obj; } int get_array_capacity(wasm_struct_obj_t obj) { wasm_array_obj_t array_ref = get_array_ref(obj); return wasm_array_obj_length(array_ref); } uint32_t get_array_element_size(wasm_array_obj_t obj) { wasm_array_type_t arr_type = (wasm_array_type_t)wasm_obj_get_defined_type((wasm_obj_t)obj); return wasm_value_type_size(arr_type->elem_type); } int32_t get_array_type_by_element(wasm_module_t wasm_module, wasm_ref_type_t *element_ref_type, bool is_mutable, wasm_array_type_t *p_array_type) { uint32_t i, type_count; type_count = wasm_get_defined_type_count(wasm_module); for (i = 0; i < type_count; i++) { wasm_defined_type_t type = wasm_get_defined_type(wasm_module, i); if (wasm_defined_type_is_array_type(type)) { bool mutable; wasm_ref_type_t arr_elem_ref_type = wasm_array_type_get_elem_type( (wasm_array_type_t)type, &mutable); if (wasm_ref_type_equal(&arr_elem_ref_type, element_ref_type, wasm_module) && (mutable == is_mutable)) { if (p_array_type) { *p_array_type = (wasm_array_type_t)type; } return i; } } } if (p_array_type) { *p_array_type = NULL; } return -1; } int32_t get_array_struct_type(wasm_module_t wasm_module, int32_t array_type_idx, wasm_struct_type_t *p_struct_type) { uint32_t i, type_count; wasm_ref_type_t res_arr_ref_type; wasm_ref_type_set_type_idx(&res_arr_ref_type, true, array_type_idx); type_count = wasm_get_defined_type_count(wasm_module); for (i = 0; i < type_count; i++) { wasm_defined_type_t type = wasm_get_defined_type(wasm_module, i); if (wasm_defined_type_is_struct_type(type) && (wasm_struct_type_get_field_count((wasm_struct_type_t)type) == 2)) { bool field1_mutable, field2_mutable; wasm_ref_type_t first_field_type = wasm_struct_type_get_field_type( (wasm_struct_type_t)type, 0, &field1_mutable); wasm_ref_type_t second_field_type = wasm_struct_type_get_field_type( (wasm_struct_type_t)type, 1, &field2_mutable); if (wasm_ref_type_equal(&first_field_type, &res_arr_ref_type, wasm_module) && second_field_type.value_type == VALUE_TYPE_I32) { if (p_struct_type) { *p_struct_type = (wasm_struct_type_t)type; } return i; } } } if (p_struct_type) { *p_struct_type = NULL; } return -1; } int32_t get_closure_struct_type(wasm_module_t wasm_module, wasm_struct_type_t *p_struct_type) { uint32_t i, type_count; wasm_defined_type_t type; wasm_ref_type_t field_type; uint32_t field_count_in_ctx = 0; wasm_struct_type_t field_defined_type; bool mut; type_count = wasm_get_defined_type_count(wasm_module); for (i = 0; i < type_count; i++) { type = wasm_get_defined_type(wasm_module, i); if (!is_ts_closure_type(wasm_module, type)) { continue; } field_type = wasm_struct_type_get_field_type((wasm_struct_type_t)type, CONTEXT_INDEX, &mut); field_defined_type = (wasm_struct_type_t)wasm_get_defined_type( wasm_module, field_type.heap_type); field_count_in_ctx = wasm_struct_type_get_field_count(field_defined_type); if (field_count_in_ctx != 0) { continue; } field_type = wasm_struct_type_get_field_type((wasm_struct_type_t)type, THIZ_INDEX, &mut); field_defined_type = (wasm_struct_type_t)wasm_get_defined_type( wasm_module, field_type.heap_type); field_count_in_ctx = wasm_struct_type_get_field_count(field_defined_type); if (field_count_in_ctx != 0) { continue; } if (p_struct_type) { *p_struct_type = (wasm_struct_type_t)type; } return i; } if (p_struct_type) { *p_struct_type = NULL; } return -1; } static uint32_t get_stringref_array_type(wasm_module_t module, wasm_array_type_t *p_array_type_t) { uint32_t i, type_count; bool is_mutable = true; type_count = wasm_get_defined_type_count(module); for (i = 0; i < type_count; i++) { wasm_defined_type_t type = wasm_get_defined_type(module, i); if (wasm_defined_type_is_array_type(type)) { bool mutable_ref = false; wasm_ref_type_t arr_elem_ref_type = wasm_array_type_get_elem_type( (wasm_array_type_t)type, &mutable_ref); if (arr_elem_ref_type.value_type == VALUE_TYPE_STRINGREF && mutable_ref == is_mutable) { if (p_array_type_t) { *p_array_type_t = (wasm_array_type_t)type; } return i; } } } if (p_array_type_t) { *p_array_type_t = NULL; } return -1; } #if WASM_ENABLE_STRINGREF != 0 wasm_struct_obj_t create_wasm_array_with_string(wasm_exec_env_t exec_env, void **ptr, uint32_t arrlen) { wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); wasm_module_t module = wasm_runtime_get_module(module_inst); wasm_local_obj_ref_t local_ref = { 0 }; wasm_array_type_t stringref_array_type = NULL; wasm_struct_type_t res_arr_struct_type = NULL; wasm_value_t val = { 0 }; val.gc_obj = NULL; uint32_t res_arr_type_idx = get_stringref_array_type(module, &stringref_array_type); bh_assert(wasm_defined_type_is_array_type( (wasm_defined_type_t)stringref_array_type)); /* get result array struct type */ get_array_struct_type(module, res_arr_type_idx, &res_arr_struct_type); bh_assert(res_arr_struct_type != NULL); bh_assert(wasm_defined_type_is_struct_type( (wasm_defined_type_t)res_arr_struct_type)); if (!ptr || !arrlen) return NULL; /* create new array */ wasm_array_obj_t new_arr = wasm_array_obj_new_with_type( exec_env, stringref_array_type, arrlen, &val); if (!new_arr) { wasm_runtime_set_exception((wasm_module_inst_t)module_inst, "alloc memory failed"); return NULL; } /* Push object to local ref to avoid being freed at next allocation */ wasm_runtime_push_local_obj_ref(exec_env, &local_ref); local_ref.val = (wasm_obj_t)new_arr; /* create_wasm_string for every element */ for (int i = 0; i < arrlen; i++) { const char *p = (const char *)((void **)ptr)[i]; void *string_struct = create_wasm_string(exec_env, p); val.gc_obj = (wasm_obj_t)string_struct; wasm_array_obj_set_elem(new_arr, i, &val); } wasm_struct_obj_t new_stringref_array_struct = wasm_struct_obj_new_with_type(exec_env, res_arr_struct_type); if (!new_stringref_array_struct) { wasm_runtime_pop_local_obj_ref(exec_env); wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), "alloc memory failed"); return NULL; } val.gc_obj = (wasm_obj_t)new_arr; wasm_struct_obj_set_field(new_stringref_array_struct, 0, &val); val.u32 = arrlen; wasm_struct_obj_set_field(new_stringref_array_struct, 1, &val); wasm_runtime_pop_local_obj_ref(exec_env); return new_stringref_array_struct; } #else wasm_struct_obj_t create_wasm_array_with_string(wasm_exec_env_t exec_env, void **ptr, uint32_t arrlen) { uint32_t arr_type_idx, string_type_idx; wasm_value_t init = { .gc_obj = NULL }, tmp_val = { 0 }, val = { .gc_obj = NULL }; wasm_array_type_t res_arr_type = NULL; wasm_struct_type_t arr_struct_type = NULL; wasm_struct_type_t string_struct_type = NULL; wasm_ref_type_t arr_ref_type; wasm_array_obj_t new_arr; wasm_local_obj_ref_t local_ref; wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); wasm_module_t module = wasm_runtime_get_module(module_inst); /* get array type_idx and the element is string */ string_type_idx = get_string_struct_type(module, &string_struct_type); wasm_ref_type_set_type_idx(&arr_ref_type, true, string_type_idx); arr_type_idx = get_array_type_by_element(module, &arr_ref_type, true, &res_arr_type); bh_assert( wasm_defined_type_is_array_type((wasm_defined_type_t)res_arr_type)); /* get result array struct type */ get_array_struct_type(module, arr_type_idx, &arr_struct_type); bh_assert( wasm_defined_type_is_struct_type((wasm_defined_type_t)arr_struct_type)); if (!ptr || !arrlen) return NULL; /* create new array */ new_arr = wasm_array_obj_new_with_type(exec_env, res_arr_type, arrlen, &init); wasm_runtime_push_local_obj_ref(exec_env, &local_ref); local_ref.val = (wasm_obj_t)new_arr; if (!new_arr) { wasm_runtime_pop_local_obj_ref(exec_env); wasm_runtime_set_exception((wasm_module_inst_t)module_inst, "alloc memory failed"); return NULL; } /* create_wasm_string for every element */ for (int i = 0; i < arrlen; i++) { const char *p = (const char *)((void **)ptr)[i]; void *string_struct = create_wasm_string(exec_env, p); val.gc_obj = (wasm_obj_t)string_struct; wasm_array_obj_set_elem(new_arr, i, &val); } wasm_struct_obj_t string_array_struct = wasm_struct_obj_new_with_type(exec_env, arr_struct_type); if (!string_array_struct) { wasm_runtime_set_exception((wasm_module_inst_t)module_inst, "alloc memory failed"); return NULL; } tmp_val.gc_obj = (wasm_obj_t)new_arr; wasm_struct_obj_set_field(string_array_struct, 0, &tmp_val); tmp_val.u32 = arrlen; wasm_struct_obj_set_field(string_array_struct, 1, &tmp_val); wasm_runtime_pop_local_obj_ref(exec_env); return string_array_struct; } #endif /* end of WASM_ENABLE_STRINGREF != 0 */ /* get_array_element_type_with_index */ #define GET_ARRAY_ELEMENT_WITH_INDEX_API(return_value, wasm_type, wasm_field) \ int get_array_element_##wasm_type##_with_index( \ wasm_struct_obj_t obj, uint32_t idx, return_value *val) \ { \ uint32_t len; \ wasm_array_obj_t arr_ref = get_array_ref(obj); \ len = get_array_length(obj); \ if (idx >= 0 && idx < len) { \ wasm_value_t value = { 0 }; \ wasm_array_obj_get_elem(arr_ref, idx, false, &value); \ *val = value.wasm_field; \ return 1; \ } \ return -1; \ } GET_ARRAY_ELEMENT_WITH_INDEX_API(double, f64, f64); GET_ARRAY_ELEMENT_WITH_INDEX_API(float, f32, f32); GET_ARRAY_ELEMENT_WITH_INDEX_API(uint64, i64, i64); GET_ARRAY_ELEMENT_WITH_INDEX_API(uint32, i32, i32); GET_ARRAY_ELEMENT_WITH_INDEX_API(void *, anyref, gc_obj); #if WASM_ENABLE_STRINGREF == 0 /* utilities for string type * string struct (WasmGC struct) +----------+ | 0:flag | +----------+ +---------------------------+ | 1:data |----->| content (WasmGC array) |\0| +----------+ +---------------------------+ ^ ^ |<------ length ------>| */ static bool is_i8_array(wasm_module_t wasm_module, bool is_mutable, wasm_ref_type_t ref_type) { if (ref_type.heap_type >= 0) { uint32_t type_idx = ref_type.heap_type; wasm_defined_type_t type = wasm_get_defined_type(wasm_module, type_idx); if (wasm_defined_type_is_array_type(type)) { bool mut; wasm_ref_type_t ref_element = wasm_array_type_get_elem_type((wasm_array_type_t)type, &mut); if (ref_element.value_type == VALUE_TYPE_I8 && mut == is_mutable) { return true; } } } return false; } int32_t get_string_array_type(wasm_module_t wasm_module, wasm_array_type_t *p_array_type_t) { uint32_t i, type_count; bool is_mutable = true; type_count = wasm_get_defined_type_count(wasm_module); for (i = 0; i < type_count; i++) { wasm_defined_type_t type = wasm_get_defined_type(wasm_module, i); if (wasm_defined_type_is_array_type(type)) { bool mutable; wasm_ref_type_t arr_elem_ref_type = wasm_array_type_get_elem_type( (wasm_array_type_t)type, &mutable); if (arr_elem_ref_type.value_type == VALUE_TYPE_I8 && mutable == is_mutable) { if (p_array_type_t) { *p_array_type_t = (wasm_array_type_t)type; } return i; } } } if (p_array_type_t) { *p_array_type_t = NULL; } return -1; } int32_t get_string_struct_type(wasm_module_t wasm_module, wasm_struct_type_t *p_struct_type) { uint32_t i, type_count; wasm_defined_type_t type; type_count = wasm_get_defined_type_count(wasm_module); for (i = 0; i < type_count; i++) { type = wasm_get_defined_type(wasm_module, i); if (!is_ts_string_type(wasm_module, type)) { continue; } if (p_struct_type) { *p_struct_type = (wasm_struct_type_t)type; } return i; } if (p_struct_type) { *p_struct_type = NULL; } return -1; } bool is_ts_string_type(wasm_module_t wasm_module, wasm_defined_type_t type) { bool is_struct_type; wasm_struct_type_t struct_type; uint32_t field_count; bool mut; wasm_ref_type_t field_type; is_struct_type = wasm_defined_type_is_struct_type(type); if (!is_struct_type) { return false; } struct_type = (wasm_struct_type_t)type; field_count = wasm_struct_type_get_field_count(struct_type); if (field_count != 2) { return false; } field_type = wasm_struct_type_get_field_type(struct_type, 0, &mut); if (field_type.value_type != VALUE_TYPE_I32 || !mut) { return false; } field_type = wasm_struct_type_get_field_type(struct_type, 1, &mut); if (!mut || !is_i8_array(wasm_module, true, field_type)) { return false; } return true; } #endif /* end of WASM_ENABLE_STRINGREF == 0 */ #if WASM_ENABLE_STRINGREF != 0 wasm_stringref_obj_t create_wasm_string(wasm_exec_env_t exec_env, const char *str) { return wasm_stringref_obj_new(exec_env, wasm_string_new_const(str, strlen(str))); } wasm_stringref_obj_t create_wasm_string_with_len(wasm_exec_env_t exec_env, const char *str, uint32_t len) { return wasm_stringref_obj_new( exec_env, wasm_string_new_with_encoding((void *)str, len, WTF16)); } uint32_t wasm_string_get_length(wasm_stringref_obj_t str_obj) { WASMString str = (WASMString)wasm_stringref_obj_get_value(str_obj); return wasm_string_encode(str, 0, wasm_string_measure(str, WTF16), NULL, NULL, WTF16); } uint32_t wasm_string_to_cstring(wasm_stringref_obj_t str_obj, char *buffer, uint32_t len) { WASMString str = (WASMString)wasm_stringref_obj_get_value(str_obj); uint32_t strlen; strlen = wasm_string_encode(str, 0, wasm_string_measure(str, WTF16), (char *)buffer, NULL, WTF16); *(char *)(buffer + strlen) = '\0'; return strlen; } #else wasm_struct_obj_t create_wasm_string(wasm_exec_env_t exec_env, const char *value) { wasm_struct_type_t string_struct_type = NULL; wasm_array_type_t string_array_type = NULL; wasm_local_obj_ref_t local_ref = { 0 }; wasm_value_t val = { 0 }; wasm_struct_obj_t new_string_struct = NULL; wasm_array_obj_t new_arr; int len = 0; char *p, *p_end; wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); wasm_module_t module = wasm_runtime_get_module(module_inst); /* get string len */ len = strlen(value); /* get struct_string_type */ get_string_struct_type(module, &string_struct_type); bh_assert(string_struct_type != NULL); bh_assert(wasm_defined_type_is_struct_type( (wasm_defined_type_t)string_struct_type)); /* wrap with string struct */ new_string_struct = wasm_struct_obj_new_with_type(exec_env, string_struct_type); if (!new_string_struct) { wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), "alloc memory failed"); return NULL; } /* Push object to local ref to avoid being freed at next allocation */ wasm_runtime_push_local_obj_ref(exec_env, &local_ref); local_ref.val = (wasm_obj_t)new_string_struct; val.i32 = 0; get_string_array_type(module, &string_array_type); new_arr = wasm_array_obj_new_with_type(exec_env, string_array_type, len, &val); if (!new_arr) { wasm_runtime_pop_local_obj_ref(exec_env); wasm_runtime_set_exception(module_inst, "alloc memory failed"); return NULL; } p = (char *)wasm_array_obj_first_elem_addr(new_arr); p_end = p + len; bh_assert(p); bh_assert(p_end); bh_memcpy_s(p, len, value, len); p += len; bh_assert(p == p_end); val.gc_obj = (wasm_obj_t)new_arr; wasm_struct_obj_set_field(new_string_struct, 1, &val); wasm_runtime_pop_local_obj_ref(exec_env); (void)p_end; return new_string_struct; } #endif /* end of WASM_ENABLE_STRINGREF != 0 */ static wasm_array_obj_t create_new_array_with_primitive_type(wasm_exec_env_t exec_env, wasm_struct_type_t *arr_struct_type, wasm_value_type_t value_type, bool is_mutable, uint32_t arrlen) { uint32_t i, type_count, arr_type_idx = 0; bool mutable; wasm_value_t init = { .gc_obj = NULL }; wasm_array_obj_t new_arr = NULL; wasm_local_obj_ref_t local_ref; wasm_ref_type_t arr_elem_ref_type; wasm_defined_type_t type; wasm_struct_type_t struct_type = NULL; wasm_array_type_t res_arr_type = NULL; wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); wasm_module_t module = wasm_runtime_get_module(module_inst); type_count = wasm_get_defined_type_count(module); for (i = 0; i < type_count; i++) { type = wasm_get_defined_type(module, i); if (!wasm_defined_type_is_array_type(type)) continue; arr_elem_ref_type = wasm_array_type_get_elem_type((wasm_array_type_t)type, &mutable); if (arr_elem_ref_type.value_type == value_type && (mutable == is_mutable)) { res_arr_type = (wasm_array_type_t)type; arr_type_idx = i; } } bh_assert( wasm_defined_type_is_array_type((wasm_defined_type_t)res_arr_type)); /* get result array struct type */ get_array_struct_type(module, arr_type_idx, &struct_type); bh_assert( wasm_defined_type_is_struct_type((wasm_defined_type_t)struct_type)); *arr_struct_type = struct_type; /* create new array */ new_arr = wasm_array_obj_new_with_type(exec_env, res_arr_type, arrlen, &init); wasm_runtime_push_local_obj_ref(exec_env, &local_ref); local_ref.val = (wasm_obj_t)new_arr; if (!new_arr) { wasm_runtime_pop_local_obj_ref(exec_env); wasm_runtime_set_exception((wasm_module_inst_t)module_inst, "alloc memory failed"); return NULL; } wasm_runtime_pop_local_obj_ref(exec_env); return new_arr; } static wasm_struct_obj_t create_wasm_array_with_type(wasm_exec_env_t exec_env, wasm_value_type_t value_type, void *ptr, uint32_t arrlen) { if (!ptr || !arrlen) return NULL; wasm_value_t tmp_val = { 0 }, val = { .gc_obj = NULL }; wasm_struct_type_t arr_struct_type; wasm_array_obj_t new_arr = NULL; wasm_struct_obj_t new_array_struct; wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); /* create new array */ new_arr = create_new_array_with_primitive_type(exec_env, &arr_struct_type, value_type, true, arrlen); /* traverse each element and assign values ​​to array elements */ for (int i = 0; i < arrlen; i++) { if (value_type == VALUE_TYPE_I32) { int32_t ele_val = ((bool *)ptr)[i]; val.i32 = ele_val; } else if (value_type == VALUE_TYPE_F64) { double ele_val = ((double *)ptr)[i]; val.f64 = ele_val; } wasm_array_obj_set_elem(new_arr, i, &val); } /* create new array struct */ new_array_struct = wasm_struct_obj_new_with_type(exec_env, arr_struct_type); if (!new_array_struct) { wasm_runtime_set_exception((wasm_module_inst_t)module_inst, "alloc memory failed"); return NULL; } tmp_val.gc_obj = (wasm_obj_t)new_arr; wasm_struct_obj_set_field(new_array_struct, 0, &tmp_val); tmp_val.u32 = arrlen; wasm_struct_obj_set_field(new_array_struct, 1, &tmp_val); return new_array_struct; } wasm_struct_obj_t create_wasm_array_with_i32(wasm_exec_env_t exec_env, void *ptr, uint32_t arrlen) { return create_wasm_array_with_type(exec_env, VALUE_TYPE_I32, ptr, arrlen); } wasm_struct_obj_t create_wasm_array_with_f64(wasm_exec_env_t exec_env, void *ptr, uint32_t arrlen) { return create_wasm_array_with_type(exec_env, VALUE_TYPE_F64, ptr, arrlen); } const char * get_str_from_string_struct(wasm_struct_obj_t obj) { wasm_array_obj_t string_arr = NULL; wasm_value_t str_val = { 0 }; wasm_struct_obj_get_field(obj, 1, false, &str_val); string_arr = (wasm_array_obj_t)str_val.gc_obj; const char *str = (const char *)wasm_array_obj_first_elem_addr(string_arr); return str; } uint32_t get_str_length_from_string_struct(wasm_struct_obj_t obj) { wasm_array_obj_t string_arr = NULL; wasm_value_t str_val = { 0 }; wasm_struct_obj_get_field(obj, 1, false, &str_val); string_arr = (wasm_array_obj_t)str_val.gc_obj; return wasm_array_obj_length(string_arr); } #if WASM_ENABLE_STRINGREF != 0 void * array_to_string(wasm_exec_env_t exec_env, void *ctx, void *obj, void *separator) { uint32_t len, i; wasm_value_t value = { 0 }; wasm_array_obj_t arr_ref = get_array_ref(obj); wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); uint32_t invoke_argc = 0; dyn_ctx_t dyn_ctx = dyntype_get_context(); dyn_value_t sep = NULL, concat_str = NULL; dyn_value_t *invoke_args = NULL; wasm_stringref_obj_t res = NULL; bool should_free_sep = false; len = get_array_length(obj); /* get separator */ if (separator) { sep = (dyn_value_t)wasm_anyref_obj_get_value( (wasm_anyref_obj_t)separator); if (dyntype_is_undefined(dyn_ctx, sep)) { sep = dyntype_new_string(dyn_ctx, wasm_stringref_obj_get_value( create_wasm_string(exec_env, ","))); should_free_sep = true; } } else { sep = dyntype_new_string( dyn_ctx, wasm_stringref_obj_get_value(create_wasm_string(exec_env, ","))); should_free_sep = true; } invoke_args = wasm_runtime_malloc(len * 2 * sizeof(dyn_value_t)); if (!invoke_args) { wasm_runtime_set_exception(module_inst, "alloc memory failed"); return NULL; } for (i = 0; i < len; i++) { wasm_array_obj_get_elem(arr_ref, i, 0, &value); if (value.gc_obj) { if (wasm_obj_is_stringref_obj(value.gc_obj)) { invoke_args[invoke_argc++] = dyntype_new_string( dyn_ctx, wasm_stringref_obj_get_value( (wasm_stringref_obj_t)value.gc_obj)); invoke_args[invoke_argc++] = sep; } else { wasm_runtime_set_exception( wasm_runtime_get_module_inst(exec_env), "array join for non-string type not implemented"); goto fail; } } } /* Remove tail seperator */ invoke_argc -= 1; invoke_args[invoke_argc] = NULL; concat_str = dyntype_invoke(dyn_ctx, "concat", invoke_args[0], invoke_argc - 1, invoke_args + 1); if (!concat_str) { wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), "concat string failed"); goto fail; } res = wasm_stringref_obj_new(exec_env, dyntype_to_string(dyn_ctx, concat_str)); dyntype_release(dyn_ctx, concat_str); fail: if (invoke_args) { for (i = 0; i < invoke_argc; i += 2) { /* only release created strings, the separator will be released * later */ dyntype_release(dyn_ctx, invoke_args[i]); } wasm_runtime_free(invoke_args); } if (should_free_sep) { dyntype_release(dyntype_get_context(), sep); } return res; } #else void * array_to_string(wasm_exec_env_t exec_env, void *ctx, void *obj, void *separator) { uint32_t len, i, result_len, sep_len; uint32_t *string_lengths; wasm_value_t value = { 0 }, field1 = { 0 }; wasm_array_obj_t new_arr, arr_ref = get_array_ref(obj); wasm_struct_type_t string_struct_type = NULL; wasm_struct_obj_t new_string_struct = NULL; wasm_array_type_t string_array_type = NULL; wasm_local_obj_ref_t local_ref = { 0 }; wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); wasm_module_t module = wasm_runtime_get_module(module_inst); char **string_addrs = NULL, *p, *p_end; char *sep = NULL; wasm_defined_type_t value_defined_type; len = get_array_length(obj); string_lengths = wasm_runtime_malloc(len * sizeof(uint32)); if (!string_lengths) { wasm_runtime_set_exception(module_inst, "alloc memory failed"); return NULL; } string_addrs = wasm_runtime_malloc(len * sizeof(char *)); if (!string_addrs) { wasm_runtime_set_exception(module_inst, "alloc memory failed"); goto fail; } /* get separator */ if (separator) { dyn_value_t js_sep = (dyn_value_t)wasm_anyref_obj_get_value( (wasm_anyref_obj_t)separator); if (!dyntype_is_undefined(ctx, js_sep)) { dyn_value_t dyn_value = (dyn_value_t)wasm_anyref_obj_get_value(separator); dyntype_to_cstring(dyntype_get_context(), dyn_value, &sep); } } for (i = 0; i < len; i++) { wasm_array_obj_get_elem(arr_ref, i, 0, &value); if (!value.gc_obj) { string_lengths[i] = 0; string_addrs[i] = ""; continue; } wasm_struct_obj_get_field((wasm_struct_obj_t)value.gc_obj, 1, false, &field1); value_defined_type = wasm_obj_get_defined_type((wasm_obj_t)value.gc_obj); if (is_ts_string_type(module, value_defined_type)) { wasm_array_obj_t str_array = (wasm_array_obj_t)field1.gc_obj; string_lengths[i] = wasm_array_obj_length(str_array); string_addrs[i] = wasm_array_obj_first_elem_addr(str_array); } else { wasm_runtime_set_exception( wasm_runtime_get_module_inst(exec_env), "array join for non-string type not implemented"); goto fail; } } result_len = 0; /* If there is no separator, it will be separated by ',' by default */ sep_len = sep ? strlen(sep) : strlen(","); for (i = 0; i < len; i++) { result_len += string_lengths[i] + sep_len; } if (len >= 1) { /* Remove separator after last character */ result_len -= sep_len; } /* Create new array for holding string contents */ value.i32 = 0; get_string_array_type(module, &string_array_type); new_arr = wasm_array_obj_new_with_type(exec_env, string_array_type, result_len, &value); if (!new_arr) { wasm_runtime_set_exception(module_inst, "alloc memory failed"); goto fail; } /* Push object to local ref to avoid being freed at next allocation */ wasm_runtime_push_local_obj_ref(exec_env, &local_ref); local_ref.val = (wasm_obj_t)new_arr; p = (char *)wasm_array_obj_first_elem_addr(new_arr); p_end = p + result_len; bh_assert(p); bh_assert(p_end); for (i = 0; i < len; i++) { uint32_t cur_string_len = string_lengths[i]; bh_memcpy_s(p, p_end - p, string_addrs[i], cur_string_len); p += cur_string_len; if (i < len - 1) { bh_memcpy_s(p, p_end - p, sep ? sep : ",", sep_len); p += sep_len; } } bh_assert(p == p_end); /* get struct_string_type */ get_string_struct_type(module, &string_struct_type); bh_assert(string_struct_type != NULL); bh_assert(wasm_defined_type_is_struct_type( (wasm_defined_type_t)string_struct_type)); /* wrap with string struct */ new_string_struct = wasm_struct_obj_new_with_type(exec_env, string_struct_type); if (!new_string_struct) { wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), "alloc memory failed"); goto fail; } value.gc_obj = (wasm_obj_t)new_arr; wasm_struct_obj_set_field(new_string_struct, 1, &value); fail: if (string_lengths) { wasm_runtime_free(string_lengths); } if (string_addrs) { wasm_runtime_free(string_addrs); } if (local_ref.val) { wasm_runtime_pop_local_obj_ref(exec_env); } if (sep) { dyntype_free_cstring(dyntype_get_context(), sep); } return new_string_struct; } #endif /* end of WASM_ENABLE_STRINGREF != 0 */ void get_static_array_info(wasm_exec_env_t exec_env, uint32_t tbl_idx, WasmArrayInfo *p_arr_info) { void *static_arr_struct = NULL; wasm_defined_type_t static_arr_arr_type = { 0 }; bool mutable = false; wasm_array_obj_t arr_ref = NULL; uint32_t arr_len = 0; wasm_ref_type_t arr_elem_ref_type = { 0 }; static_arr_struct = (wasm_struct_obj_t)wamr_utils_get_table_element(exec_env, tbl_idx); arr_ref = get_array_ref(static_arr_struct); arr_len = get_array_length(static_arr_struct); static_arr_arr_type = wasm_obj_get_defined_type((wasm_obj_t)arr_ref); arr_elem_ref_type = wasm_array_type_get_elem_type( (wasm_array_type_t)static_arr_arr_type, &mutable); p_arr_info->ref = arr_ref; bh_memcpy_s(&p_arr_info->element_type, sizeof(wasm_ref_type_t), &arr_elem_ref_type, sizeof(wasm_ref_type_t)); p_arr_info->lengh = arr_len; } int get_prop_index_of_struct(wasm_exec_env_t exec_env, const char *prop, wasm_obj_t *wasm_obj, wasm_ref_type_t *field_type) { wasm_module_inst_t module_inst; bool is_mut; wasm_function_inst_t func; wasm_struct_obj_t wasm_struct_obj; WASMValue vtable_value = { 0 }; WASMValue meta = { 0 }; uint32_t argc = 3, argv[3] = { 0 }, offset; wasm_struct_type_t struct_type; wasm_struct_type_t vtable_type; int property_flag = -1; int property_index = -1; module_inst = wasm_runtime_get_module_inst(exec_env); wasm_struct_obj = (wasm_struct_obj_t)(*wasm_obj); wasm_struct_obj_get_field(wasm_struct_obj, 0, false, &vtable_value); wasm_struct_obj_get_field((wasm_struct_obj_t)vtable_value.gc_obj, 0, false, &meta); struct_type = (wasm_struct_type_t)wasm_obj_get_defined_type(*wasm_obj); func = wasm_runtime_lookup_function(module_inst, "find_property_flag_and_index"); bh_assert(func); argv[0] = meta.i32; offset = wasm_runtime_addr_native_to_app(module_inst, (void *)prop); argv[1] = offset; argv[2] = ALL; wasm_runtime_call_wasm(exec_env, func, argc, argv); if (argv[0] != -1) { property_flag = argv[0] & META_FLAG_MASK; property_index = (argv[0] & META_INDEX_MASK) >> 4; if (property_flag == METHOD) { vtable_type = (wasm_struct_type_t)wasm_obj_get_defined_type( vtable_value.gc_obj); *field_type = wasm_struct_type_get_field_type( vtable_type, property_index, &is_mut); } else if (property_flag == FIELD) { *field_type = wasm_struct_type_get_field_type( struct_type, property_index, &is_mut); } } return property_index; } /**********Utils for search field value of object through meta * information*************/ int32 get_meta_fields_count(void *meta) { return *(int32 *)(meta + OFFSET_OF_COUNT); } static inline void * get_meta_field_by_index(void *meta, int32 index) { return (meta + OFFSET_OF_META_FIELDS + index * SIZEOF_META_FIELD); } static inline enum field_flag get_meta_field_flag(void *meta_field) { int flag = *((int32 *)(meta_field + OFFSET_OF_FIELD_FLAG_AND_INDEX)) & META_FLAG_MASK; return (enum field_flag)flag; } static inline int32 get_meta_field_index(void *meta_field) { return (*((int32 *)(meta_field + OFFSET_OF_FIELD_FLAG_AND_INDEX)) & META_INDEX_MASK) >> 4; } static inline int32 get_meta_field_type(void *meta_field) { return *((int32 *)(meta_field + OFFSET_OF_FIELD_TYPE)); } static inline int32 get_meta_field_name(void *meta_field) { return *((int32 *)meta_field); } static int32 get_object_field_index_by_mata(wasm_exec_env_t exec_env, void *meta, const char *field_name, enum field_flag flag, ts_value_type_t *field_type) { int32 count; void *meta_field; enum field_flag meta_field_flag; int32 meta_field_name_offset; const char *meta_field_name; int32 meta_field_index; int32 field_type_id; count = get_meta_fields_count(meta); meta_field_index = -1; for (int index = 0; index < count; index++) { meta_field = get_meta_field_by_index(meta, index); meta_field_flag = get_meta_field_flag(meta_field); meta_field_name_offset = get_meta_field_name(meta_field); meta_field_name = wasm_runtime_addr_app_to_native( wasm_runtime_get_module_inst(exec_env), meta_field_name_offset); if (meta_field_flag == flag && strcmp(field_name, meta_field_name) == 0) { meta_field_index = get_meta_field_index(meta_field); if (field_type) { field_type_id = get_meta_field_type(meta_field); if (field_type_id >= CUSTOM_TYPE_BEGIN) { *field_type = TS_OBJECT; } else { *field_type = (ts_value_type_t)field_type_id; } } break; } } return meta_field_index; } int get_object_field(wasm_exec_env_t exec_env, wasm_obj_t obj, const char *field_name, enum field_flag flag, ts_value_t *field_value) { void *meta_addr; int32 field_index; wasm_struct_obj_t vtable_struct; WASMValue vtable_value = { 0 }; wasm_value_t value = { 0 }; wasm_struct_obj_get_field((wasm_struct_obj_t)obj, 0, false, &vtable_value); vtable_struct = (wasm_struct_obj_t)vtable_value.gc_obj; /* get meta addr of obj */ meta_addr = get_meta_of_object(exec_env, obj); /* get field index */ field_index = get_object_field_index_by_mata( exec_env, meta_addr, field_name, flag, &field_value->type); if (field_index == -1) { return -1; } if (flag == FIELD) { wasm_struct_obj_get_field((wasm_struct_obj_t)obj, field_index, false, &value); } else { wasm_struct_obj_get_field(vtable_struct, field_index, false, &value); } if (field_value->type == TS_BOOLEAN || field_value->type == TS_INT) { field_value->of.i32 = value.i32; } else if (field_value->type == TS_NUMBER) { field_value->of.f64 = value.f64; } else { field_value->of.ref = value.gc_obj; } return 0; } void * get_meta_of_object(wasm_exec_env_t exec_env, wasm_obj_t obj) { wasm_struct_obj_t struct_obj; wasm_struct_obj_t vtable_struct; WASMValue vtable_value = { 0 }; wasm_value_t meta = { 0 }; void *meta_addr; /* get meta addr */ struct_obj = (wasm_struct_obj_t)obj; wasm_struct_obj_get_field(struct_obj, 0, false, &vtable_value); vtable_struct = (wasm_struct_obj_t)vtable_value.gc_obj; wasm_struct_obj_get_field(vtable_struct, 0, false, &meta); meta_addr = wasm_runtime_addr_app_to_native( wasm_runtime_get_module_inst(exec_env), meta.i32); return meta_addr; } const char * get_field_name_from_meta_index(wasm_exec_env_t exec_env, void *meta, enum field_flag flag, uint32_t index) { int32 count; void *meta_field; enum field_flag meta_field_flag; int32 meta_field_name_offset; const char *meta_field_name; count = get_meta_fields_count(meta); if (index >= 0 && index < count) { meta_field = get_meta_field_by_index(meta, index); meta_field_flag = get_meta_field_flag(meta_field); meta_field_name_offset = get_meta_field_name(meta_field); meta_field_name = wasm_runtime_addr_app_to_native( wasm_runtime_get_module_inst(exec_env), meta_field_name_offset); if (meta_field_name && meta_field_flag == flag) return meta_field_name; } return NULL; } ================================================ FILE: runtime-library/utils/type_utils.h ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #ifndef __TYPE_UTILS_H_ #define __TYPE_UTILS_H_ #include "gc_export.h" #include "gc_object.h" void dynamic_object_finalizer(wasm_anyref_obj_t obj, void *data); /* Convert host pointer to anyref */ #define RETURN_BOX_ANYREF(ptr, dyn_ctx) \ do { \ wasm_anyref_obj_t any_obj = \ (wasm_anyref_obj_t)wasm_anyref_obj_new(exec_env, ptr); \ if (!any_obj) { \ wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), \ "alloc memory failed"); \ return NULL; \ } \ wasm_obj_set_gc_finalizer( \ exec_env, (wasm_obj_t)any_obj, \ (wasm_obj_finalizer_t)dynamic_object_finalizer, dyn_ctx); \ return any_obj; \ } while (0) #define BOX_ANYREF(ptr) wasm_anyref_obj_new(exec_env, ptr) /* Convert anyref to host pointer */ #define UNBOX_ANYREF(anyref) \ (dyn_value_t) wasm_anyref_obj_get_value((wasm_anyref_obj_t)anyref) #define ENV_PARAM_LEN 2 #define GET_ELEM_FROM_CLOSURE(closure) \ wasm_value_t context = { 0 }, thiz = { 0 }, func_obj = { 0 }; \ wasm_struct_obj_get_field(closure, CONTEXT_INDEX, false, &context); \ wasm_struct_obj_get_field(closure, THIZ_INDEX, false, &thiz); \ wasm_struct_obj_get_field(closure, FUNC_INDEX, false, &func_obj); #define POPULATE_ENV_ARGS(argv, total_size, occupied_slots, context, thiz) \ /* arg0: context */ \ bh_memcpy_s(argv, total_size, &context.gc_obj, sizeof(void *)); \ occupied_slots += sizeof(void *) / sizeof(uint32); \ /* arg1: thiz */ \ bh_memcpy_s(argv + occupied_slots, \ total_size - occupied_slots * sizeof(uint32), &thiz.gc_obj, \ sizeof(void *)); \ occupied_slots += sizeof(void *) / sizeof(uint32); enum closure_index { CONTEXT_INDEX = 0, THIZ_INDEX = 1, FUNC_INDEX = 2, }; enum field_flag { FIELD = 0, METHOD = 1, GETTER = 2, SETTER = 3, ALL = 4, }; typedef enum ts_value_type_t { TS_OBJECT = 0, TS_NULL = 3, TS_INT = 5, TS_NUMBER = 6, TS_BOOLEAN = 7, TS_STRING = 9, TS_ANY = 10, TS_ARRAY = 16, TS_FUNCTION = 24, } ts_value_type_t; typedef struct ts_value_t { ts_value_type_t type; /** * Type of the ts value, if it's TS_BOOLEAN or TS_INT, value can be retrieved from of.i32, * if it's TS_NUMBER, value can be retrived from f64, otherwise get value from of.ref. */ union { int32_t i32; double f64; void *ref; } of; } ts_value_t; /* whether the type is struct(struct_func) */ bool is_ts_closure_type(wasm_module_t wasm_module, wasm_defined_type_t type); /* whether the type is struct(array_i32) */ bool is_ts_array_type(wasm_module_t wasm_module, wasm_defined_type_t type); /* Helper to get common used fields */ uint32_t get_array_length(wasm_struct_obj_t obj); wasm_array_obj_t get_array_ref(wasm_struct_obj_t obj); int get_array_capacity(wasm_struct_obj_t obj); uint32_t get_array_element_size(wasm_array_obj_t obj); /* Type reflection */ int32_t get_array_type_by_element(wasm_module_t wasm_module, wasm_ref_type_t *element_ref_type, bool is_mutable, wasm_array_type_t *p_array_type); int32_t get_array_struct_type(wasm_module_t wasm_module, int32_t array_type_idx, wasm_struct_type_t *p_struct_type); int32_t get_closure_struct_type(wasm_module_t wasm_module, wasm_struct_type_t *p_struct_type); wasm_struct_obj_t create_wasm_array_with_string(wasm_exec_env_t exec_env, void **ptr, uint32_t arrlen); /* get string struct type*/ int32_t get_string_struct_type(wasm_module_t wasm_module, wasm_struct_type_t *p_struct_type); /* get string array type*/ int32_t get_string_array_type(wasm_module_t wasm_module, wasm_array_type_t *p_array_type_t); bool is_ts_string_type(wasm_module_t wasm_module, wasm_defined_type_t type); #if WASM_ENABLE_STRINGREF != 0 /* create wasm string from c string*/ wasm_stringref_obj_t create_wasm_string(wasm_exec_env_t exec_env, const char *str); wasm_stringref_obj_t create_wasm_string_with_len(wasm_exec_env_t exec_env, const char *str, uint32_t len); /* get required space to store string contents, including the space for NULL * terminator */ uint32_t wasm_string_get_length(wasm_stringref_obj_t str_obj); /* covert wasm string to NULL terminated cstring */ uint32_t wasm_string_to_cstring(wasm_stringref_obj_t str_obj, char *buffer, uint32_t len); #else wasm_struct_obj_t create_wasm_string(wasm_exec_env_t exec_env, const char *value); #endif /* combine elements of an array to an string */ void * array_to_string(wasm_exec_env_t exec_env, void *ctx, void *obj, void *separator); typedef struct WasmArrayInfo { wasm_array_obj_t ref; wasm_ref_type_t element_type; uint32_t lengh; } WasmArrayInfo; void get_static_array_info(wasm_exec_env_t exec_env, uint32_t tbl_idx, WasmArrayInfo *p_arr_info); /* get property of a struct * result: -2: not a static object, -1: error: else: static object index */ int get_prop_index_of_struct(wasm_exec_env_t exec_env, const char *prop, wasm_obj_t *wasm_obj, wasm_ref_type_t *field_type); /** * @brief Access object field through meta information * * @param obj object * @param field_name the specified field name * @param flag field flag * @param field_value if the field is found, it represents field value * @result 0: if field is found, -1: if field is not found */ int get_object_field(wasm_exec_env_t exec_env, wasm_obj_t obj, const char *field_name, enum field_flag flag, ts_value_t *field_value); /* get str from a string struct */ const char * get_str_from_string_struct(wasm_struct_obj_t obj); /* get str length from a string struct */ uint32_t get_str_length_from_string_struct(wasm_struct_obj_t obj); /** * @brief get_array_element_type_with_index: * @param obj: array struct obj * @param idx: find element index * @param val: return value pointer * @result 1: if element is find, -1: if element is not found */ int get_array_element_f64_with_index(wasm_struct_obj_t obj, uint32_t idx, double *val); int get_array_element_f32_with_index(wasm_struct_obj_t obj, uint32_t idx, float *val); int get_array_element_i64_with_index(wasm_struct_obj_t obj, uint32_t idx, uint64 *val); int get_array_element_i32_with_index(wasm_struct_obj_t obj, uint32_t idx, uint32_t *val); int get_array_element_anyref_with_index(wasm_struct_obj_t obj, uint32_t idx, void **val); /* create wasm_array and it's element type is f64 */ wasm_struct_obj_t create_wasm_array_with_f64(wasm_exec_env_t exec_env, void *ptr, uint32_t arrlen); /* create wasm_array and it's element type is i32 */ wasm_struct_obj_t create_wasm_array_with_i32(wasm_exec_env_t exec_env, void *ptr, uint32_t arrlen); /** * @brief get meta info addr through wasm_obj_t * @param obj object * @result : meta info addr */ void * get_meta_of_object(wasm_exec_env_t exec_env, wasm_obj_t obj); /** * @brief get member count through meta info addr. * @param meta meta info addr. * @result : member field count. */ int32 get_meta_fields_count(void *meta); /** * @brief get meta info and field member count through wasm_obj_t * @param meta meta pointer * @param flag field flag * @param index get field name through index * @result : return field name if call success or return NULL. */ const char * get_field_name_from_meta_index(wasm_exec_env_t exec_env, void *meta, enum field_flag flag, uint32_t index); #endif /* end of __TYPE_UTILS_H_ */ ================================================ FILE: runtime-library/utils/wamr_utils.c ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ /* This file provide APIs to access WAMR internal data structures, this should * be removed if these APIs are directly implemented in WAMR */ #include "wasm_exec_env.h" #include "wasm_runtime.h" #if WASM_ENABLE_AOT != 0 #include "aot_runtime.h" #endif #include "wasm_runtime_common.h" void * wamr_utils_get_table_element(WASMExecEnv *exec_env, uint32_t index) { WASMModuleInstanceCommon *module_inst = wasm_exec_env_get_module_inst(exec_env); #if WASM_ENABLE_INTERP != 0 if (module_inst->module_type == Wasm_Module_Bytecode) { WASMModuleInstance *wasm_module_inst = (WASMModuleInstance *)module_inst; WASMTableInstance *table_inst = wasm_module_inst->tables[0]; return table_inst->elems[index]; } #endif #if WASM_ENABLE_AOT != 0 if (module_inst->module_type == Wasm_Module_AoT) { WASMModuleInstance *aot_module_inst = (WASMModuleInstance *)module_inst; AOTModule *module = (AOTModule *)aot_module_inst->module; AOTTableInstance *table_inst = (AOTTableInstance *)(aot_module_inst->global_data + module->global_data_size); return table_inst->elems[index]; } #endif return NULL; } ================================================ FILE: runtime-library/utils/wamr_utils.h ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "wasm_export.h" /** * @brief Get element from wasm table by index * * @param exec_env wasm execution environment * @param index element index * * @return the element stored in the table slot, for GC objects, it's wasm_obj_t */ void * wamr_utils_get_table_element(wasm_exec_env_t exec_env, uint32_t index); ================================================ FILE: runtime-library/wamr_config.cmake ================================================ # # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # set (WAMR_DIR ${CMAKE_CURRENT_LIST_DIR}/deps/wamr-gc) if (NOT DEFINED WAMR_BUILD_PLATFORM) set (WAMR_BUILD_PLATFORM "linux") endif() set (WAMR_BUILD_INTERP 1) set (WAMR_BUILD_LIBC_BUILTIN 1) set (WAMR_BUILD_GC 1) set (WAMR_BUILD_STRINGREF 1) add_definitions(-DWASM_TABLE_MAX_SIZE=10240) if (NOT DEFINED WAMR_BUILD_TARGET) set (WAMR_BUILD_TARGET X86_64) endif() if (NOT DEFINED WAMR_BUILD_FAST_INTERP) set (WAMR_BUILD_FAST_INTERP 1) endif() if (NOT DEFINED WAMR_BUILD_SIMD) # Enable SIMD by default set (WAMR_BUILD_SIMD 1) endif () if (NOT DEFINED WAMR_BUILD_AOT) # Enable AOT by default set (WAMR_BUILD_AOT 1) endif () ## stringref set(STRINGREF_DIR ${CMAKE_CURRENT_LIST_DIR}/stringref) if (NOT USE_SIMPLE_LIBDYNTYPE EQUAL 1) set(WAMR_STRINGREF_IMPL_SOURCE ${STRINGREF_DIR}/stringref_qjs.c ) else() set(WAMR_STRINGREF_IMPL_SOURCE ${STRINGREF_DIR}/stringref_simple.c ) endif () if (WAMR_GC_IN_EVERY_ALLOCATION EQUAL 1) message("* Garbage collection in every allocation: on") # Force GC in every allocation during testing add_definitions(-DGC_IN_EVERY_ALLOCATION=1) endif() if (DEFINED WAMR_GC_HEAP_SIZE) message("* Set WAMR gc heap size: ${WAMR_GC_HEAP_SIZE}") add_definitions(-DGC_HEAP_SIZE_DEFAULT=${WAMR_GC_HEAP_SIZE}) endif() include(${WAMR_DIR}/build-scripts/runtime_lib.cmake) ================================================ FILE: scripts/LICENSE.txt ================================================ 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: You must give any other recipients of the Work or Derivative Works a copy of this License; and You must cause any modified files to carry prominent notices stating that You changed the files; and 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 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 ================================================ FILE: scripts/copy_lib_files.js ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const repo_root_dir = path.resolve(__dirname, '..'); let distDir = path.resolve(repo_root_dir, 'build'); const path_to_copy = [ 'lib/builtin', 'src/backend/binaryen/lib/interface' ] if (process.argv.length > 2) { distDir = path.resolve(repo_root_dir, process.argv[2]); } path_to_copy.forEach((p) => { if (!fs.existsSync(path.join(repo_root_dir, p))) { fs.mkdirSync(path.join(repo_root_dir, p), { recursive: true }) } const cur_dst_path = path.join(distDir, p) fs.cpSync(path.join(repo_root_dir, p), cur_dst_path, { recursive: true }) }) ================================================ FILE: scripts/gdb/.gitignore ================================================ __pycache__ ================================================ FILE: scripts/gdb/constants.py ================================================ # # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # # color constants RED = '\033[1;91m' GREEN = '\033[1;92m' YELLOW = '\033[1;93m' BLUE = '\033[1;94m' PURPLE = '\033[1;95m' CYAN = '\033[1;96m' WHITE = '\033[1;97m' ENDC = '\033[0m' HMU_HEADER_SIZE = 4 HMU_HEADER_TYPE = 'uint32_t' HMU_SIZE_SIZE = 27 HMU_SIZE_OFFSET = 0 HMU_UT_SIZE = 2 HMU_UT_OFFSET = 30 HMU_P_OFFSET = 29 HMU_WO_MB_OFFSET = 28 HMU_UT_MAP = ['HMU_FM', 'HMU_FC', 'HMU_VO', 'HMU_WO',] TYPE_KIND = ['WASM_TYPE_FUNC', 'WASM_TYPE_STRUCT', 'WASM_TYPE_ARRAY'] WASM_OBJ_EXTERNREF_OBJ_FLAG = (1 << 0) WASM_OBJ_ANYREF_OBJ_FLAG = (1 << 1) WASM_ARRAY_LENGTH_SHIFT = 2 WASM_ARRAY_ELEM_SIZE_MASK = 3 WASM_TYPE_MAP = { # Reference Types 0x65: 'REF_TYPE_NULLREF', 0x66: 'REF_TYPE_ARRAYREF', 0x67: 'REF_TYPE_STRUCTREF', 0x68: 'REF_TYPE_NULLEXTERNREF', 0x69: 'REF_TYPE_NULLFUNCREF', 0x6A: 'REF_TYPE_I31REF', 0x6B: 'REF_TYPE_HT_NON_NULLABLE', 0x6C: 'REF_TYPE_HT_NULLABLE', 0x6D: 'REF_TYPE_EQREF', 0x6E: 'REF_TYPE_ANYREF', 0x6F: 'REF_TYPE_EXTERNREF', 0x70: 'REF_TYPE_FUNCREF', # Packed Types 0x7A: 'PACKED_TYPE_I8', 0x79: 'PACKED_TYPE_I16', # Value Types 0x7F: 'VALUE_TYPE_I32', 0X7E: 'VALUE_TYPE_I64', 0x7D: 'VALUE_TYPE_F32', 0x7C: 'VALUE_TYPE_F64', 0x7B: 'VALUE_TYPE_V128', 0x40: 'VALUE_TYPE_VOID', } ================================================ FILE: scripts/gdb/utils.py ================================================ # # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # import gdb from constants import * ### common utils def get_bits(val, size, offset = 0): mask = ((1 << size) - 1) << offset return (val & mask) >> offset def get_bit(val, offset = 0): return get_bits(val, 1, offset) ### hmu utils def obj_to_hmu(obj): return int(obj) - HMU_HEADER_SIZE def hmu_to_obj(hmu): return int(hmu) + HMU_HEADER_SIZE def hmu_get_size(hmu): return get_bits(hmu, HMU_SIZE_SIZE, HMU_SIZE_OFFSET) << 3 def hmu_get_ut(hmu): return HMU_UT_MAP[get_bits(hmu, HMU_UT_SIZE, HMU_UT_OFFSET)] def hmu_get_pinuse(hmu): return get_bit(HMU_P_OFFSET) def hmu_get_vo_marked(hmu): return get_bit(HMU_WO_MB_OFFSET) ## rtt utils def obj_get_rtt_ref(obj): return obj.cast( gdb.lookup_type('wasm_obj_t') ).dereference()['header'].cast( gdb.lookup_type('WASMRttTypeRef') ) def rtt_get_defined_type_ref(rtt): return rtt['defined_type'].cast( gdb.lookup_type('wasm_defined_type_t') ) ================================================ FILE: scripts/gdb/wamr_dbg.py ================================================ # # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # import gdb import os import sys current_dir = os.path.dirname(os.path.abspath(__file__)) sys.path.append(current_dir) from utils import * from constants import * from wamr_objects import * from wamr_types import * from wamr_exec_env import * # enable auto completion for gdb # import rlcompleter, readline; readline.parse_and_bind("tab: complete") def _get_ref(arg): if (type(arg) == int): ref = gdb.Value(arg) elif (type(arg) == str): ref = gdb.parse_and_eval(arg) else: ref = arg return ref def wamr_get_type(arg): ref = _get_ref(arg) return WASMType.create_type(ref) def wamr_type_dump(arg): wasm_type = wamr_get_type(arg) if (wasm_type): print(wasm_type) def wamr_get_gc_obj(arg): ref = _get_ref(arg) return WASMObj.create_obj(ref) def wamr_gc_obj_dump(arg): wasm_obj = wamr_get_gc_obj(arg) if (wasm_obj): print(wasm_obj) def wamr_get_exec_env(arg): ref = _get_ref(arg) return WASMExecEnv(ref) def wamr_exec_env_dump(arg): wasm_exec = wamr_get_exec_env(arg) if (wasm_exec): print(wasm_exec) def wamr_get_interp_frame(arg): ref = _get_ref(arg) return WASMInterpFrame(ref) def wamr_interp_frame_dump(arg): wasm_frame = wamr_get_interp_frame(arg) if (wasm_frame): print(wasm_frame) def wamr_interp_frame_dump_all(arg): wasm_frame = wamr_get_interp_frame(arg) while (wasm_frame): print(wasm_frame) wasm_frame = wasm_frame.get_prev_frame() def wamr_exec_env_dump_all_interp_frame(arg): exec_env = wamr_get_exec_env(arg) if (exec_env): cur_frame = exec_env.get_cur_frame() while (cur_frame): print(cur_frame) cur_frame = cur_frame.get_prev_frame() def wamr_operand_stack_get_addr(arg1, arg2): wasm_frame = wamr_get_interp_frame(arg1) index = _get_ref(arg2) if (wasm_frame): return wasm_frame.get_stack_addr(int(index)) def wamr_operand_stack_get_i32(arg1, arg2): wasm_frame = wamr_get_interp_frame(arg1) index = _get_ref(arg2) if (wasm_frame): return wasm_frame.get_stack_i32(int(index)) def wamr_operand_stack_get_i64(arg1, arg2): wasm_frame = wamr_get_interp_frame(arg1) index = _get_ref(arg2) if (wasm_frame): return wasm_frame.get_stack_i64(int(index)) def wamr_operand_stack_get_f32(arg1, arg2): wasm_frame = wamr_get_interp_frame(arg1) index = _get_ref(arg2) if (wasm_frame): return wasm_frame.get_stack_f32(int(index)) def wamr_operand_stack_get_f64(arg1, arg2): wasm_frame = wamr_get_interp_frame(arg1) index = _get_ref(arg2) if (wasm_frame): return wasm_frame.get_stack_f64(int(index)) def wamr_operand_stack_get_ref(arg1, arg2): wasm_frame = wamr_get_interp_frame(arg1) index = _get_ref(arg2) if (wasm_frame): return wasm_frame.get_stack_ref(int(index)) def wamr_operand_stack_get_obj(arg1, arg2): ref = wamr_operand_stack_get_ref(arg1, arg2) wamr_gc_obj_dump(ref) def wamr_get_function(arg): ref = _get_ref(arg) return WASMFunction(ref) def wamr_function_dump(arg): wasm_frame = wamr_get_function(arg) if (wasm_frame): print(wasm_frame) # register commands gdb.execute("define wamr_type_dump\n" + "python wamr_type_dump('$arg0')\n" + "end") gdb.execute("define wamr_function_dump\n" + "python wamr_function_dump('$arg0')\n" + "end") gdb.execute("define wamr_gc_obj_dump\n" + "python wamr_gc_obj_dump('$arg0')\n" + "end") gdb.execute("define wamr_exec_env_dump\n" + "python wamr_exec_env_dump('$arg0')\n" + "end") gdb.execute("define wamr_exec_env_dump_all_interp_frame\n" + "python wamr_exec_env_dump_all_interp_frame('$arg0')\n" + "end") ## frame operation gdb.execute("define wamr_interp_frame_dump\n" + "python wamr_interp_frame_dump('$arg0')\n" + "end") gdb.execute("define wamr_interp_frame_dump_all\n" + "python wamr_interp_frame_dump_all('$arg0')\n" + "end") gdb.execute("define wamr_operand_stack_get_addr\n" + "python print(wamr_operand_stack_get_addr('$arg0', '$arg1'))\n" + "end") gdb.execute("define wamr_operand_stack_get_i32\n" + "python print(wamr_operand_stack_get_i32('$arg0', '$arg1'))\n" + "end") gdb.execute("define wamr_operand_stack_get_i64\n" + "python print(wamr_operand_stack_get_i64('$arg0', '$arg1'))\n" + "end") gdb.execute("define wamr_operand_stack_get_f32\n" + "python print(wamr_operand_stack_get_f32('$arg0', '$arg1'))\n" + "end") gdb.execute("define wamr_operand_stack_get_f64\n" + "python print(wamr_operand_stack_get_f64('$arg0', '$arg1'))\n" + "end") gdb.execute("define wamr_operand_stack_get_ref\n" + "python print(wamr_operand_stack_get_ref('$arg0', '$arg1'))\n" + "end") gdb.execute("define wamr_operand_stack_get_obj\n" + "python print(wamr_operand_stack_get_obj('$arg0', '$arg1'))\n" + "end") ================================================ FILE: scripts/gdb/wamr_exec_env.py ================================================ # # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # import gdb from constants import * from utils import * from wamr_types import * class WASMFunction: def __init__(self, ref): self.ref = ref value = ref.cast( gdb.lookup_type('WASMFunctionInstance').pointer() ) self.func = value.dereference() self.is_import_func = bool(self.func['is_import_func']) self.param_count = int(self.func['param_count']) self.local_count = int(self.func['local_count']) self.param_cell_num = int(self.func['param_cell_num']) self.ret_cell_num = int(self.func['ret_cell_num']) self.local_cell_num = int(self.func['local_cell_num']) self.local_offsets = self.func['local_offsets'] self.param_types = self.func['param_types'] self.local_types = self.func['local_types'] if (self.is_import_func): self.import_func = self.func['u']['func_import'] self.import_module = self.import_func['module_name'] self.import_field = self.import_func['field_name'] self.func_type = WASMFuncType(self.import_func['func_type']) else: self.func_type = WASMFuncType(self.func['u']['func']['func_type']) self.func_fields = [field.name for field in self.func.type.fields()] def get_max_stack_cell_num(self): max_local_slot = self.param_cell_num + self.local_cell_num if (self.is_import_func): return max_local_slot else: return max_local_slot + int(self.func['u']['func']['max_stack_cell_num']) def __str__(self): if (self.func.address == 0): return f'{GREEN}WASMFunctionInstance: null\n{ENDC}' import_name = f'{self.import_module}|{self.import_field}' if self.is_import_func else "no" return f'{GREEN}WASMFunctionInstance: {hex(self.ref)}\n' + \ f' * import func: {import_name}\n' + \ f' * local_count: {self.local_count}\n' + \ f' * ret_cell_num: {self.ret_cell_num}\n' + \ f'{ENDC}\n' + \ str(self.func_type) class WASMInterpFrame: def __init__(self, ref): self.ref = ref value = ref.cast( gdb.lookup_type('WASMInterpFrame').pointer() ) self.interp_frame = value.dereference() self.fast_interp = False # mode detection self.interp_frame_fields = [field.name for field in self.interp_frame.type.fields()] if ('ret_offset' in self.interp_frame_fields): self.fast_interp = True def _get_feature_description(self): features = '' features += f' * fast_interp: {"on" if self.fast_interp else "off"}\n' return features def get_prev_frame(self): ref = self.interp_frame['prev_frame'] if (ref != 0): return WASMInterpFrame(ref) return None def _get_operand_stack(self): return self.interp_frame['lp'] def get_stack_addr(self, index): return self._get_operand_stack()[index].address def get_stack_i32(self, index): return self._get_operand_stack()[index] def get_stack_i64(self, index): addr = self.get_stack_addr(index) return gdb.Value(addr).cast(gdb.lookup_type('int64_t').pointer()).dereference() def get_stack_f32(self, index): addr = self.get_stack_addr(index) return gdb.Value(addr).cast(gdb.lookup_type('float').pointer()).dereference() def get_stack_f64(self, index): addr = self.get_stack_addr(index) return gdb.Value(addr).cast(gdb.lookup_type('double').pointer()).dereference() def get_stack_ref(self, index): addr = self.get_stack_addr(index) return gdb.Value(addr).cast(gdb.lookup_type('void').pointer().pointer()).dereference() def _get_conditional_info(self): info = '' if ('ret_offset' in self.interp_frame_fields): info += f' * ret_offset: {int(self.interp_frame["ret_offset"])}\n' if ('lp' in self.interp_frame_fields): info += f' * lp: {self.interp_frame["lp"]}\n' if ('operand' in self.interp_frame_fields): info += f' * operand: {self.interp_frame["operand"]}\n' if ('sp_bottom' in self.interp_frame_fields): info += f' * sp_bottom: {hex(int(self.interp_frame["sp_bottom"]))}\n' if ('sp_boundary' in self.interp_frame_fields): info += f' * sp_boundary: {hex(int(self.interp_frame["sp_boundary"]))}\n' if ('sp' in self.interp_frame_fields): info += f' * sp: {hex(int(self.interp_frame["sp"]))}\n' if ('csp_bottom' in self.interp_frame_fields): info += f' * csp_bottom: {hex(int(self.interp_frame["csp_bottom"]))}\n' if ('csp_boundary' in self.interp_frame_fields): info += f' * csp_boundary: {hex(int(self.interp_frame["csp_boundary"]))}\n' if ('csp' in self.interp_frame_fields): info += f' * csp: {hex(int(self.interp_frame["csp"]))}\n' if ('frame_ref' in self.interp_frame_fields): info += f' * frame_ref: {hex(int(self.interp_frame["frame_ref"]))}\n' if (int(self.interp_frame["function"]) != 0): func = WASMFunction(self.interp_frame["function"]) max_cell_num = func.get_max_stack_cell_num() param_cell_num = func.param_cell_num local_cell_num = func.local_cell_num # dump operand stack content frame_ref_array = None lp_array = None ## Check if GC enabled if ('frame_ref' in self.interp_frame_fields): # fast interpreter frame_ref_array = self.interp_frame['frame_ref'].cast( gdb.lookup_type('uint8_t').pointer() ) else: # classic interpreter try: func = gdb.parse_and_eval('get_frame_ref') if func.type.code == gdb.TYPE_CODE_FUNC: res = func(self.ref) frame_ref_array = res.cast( gdb.lookup_type('uint8_t').pointer() ) except gdb.error: pass if ('operand' in self.interp_frame_fields): lp_array = self.interp_frame['lp'].cast( gdb.lookup_type('uint32_t').pointer() ) else: lp_array = self.interp_frame['lp'] if (max_cell_num > 0): if (frame_ref_array or lp_array): data = [] value_len = 15 header = [f'slot', f'{"value":^{value_len}}'] row_seperator = f'\t+{"—" * 6}+—{"—" * value_len}—+\n' if (frame_ref_array): header.append('ref') row_seperator = f'\t+{"—" * 6}+—{"—" * value_len}—+{"—" * 5}+\n' for i in range(max_cell_num): row = [f'{i:^4}'] if lp_array: row.append(f'{int(lp_array[i]):^{value_len}}') if (frame_ref_array): row.append(f'{int(frame_ref_array[i]):^3}') data.append(row) info += f' * operand stack:\n' info += row_seperator info += f'\t| {" | ".join(header)} |\n' info += row_seperator for i in range(len(data)): row = data[i] info += f'\t| {" | ".join([str(x) for x in row])} |' if (i == param_cell_num - 1): info += ' <--- param end' if (i == param_cell_num + local_cell_num - 1): info += ' <--- local end' if (i == param_cell_num + local_cell_num): info += ' <--- dynamic space start' if ('sp' in self.interp_frame_fields and i == int(self.interp_frame['sp'])): info += f'{PURPLE} <--- sp{GREEN}' if ('ret_offset' in self.interp_frame_fields and i == int(self.interp_frame['ret_offset'])): info += f'{PURPLE} <--- ret_offset{GREEN}' info += '\n' info += row_seperator return info def __str__(self) -> str: return f'{GREEN}WASMInterpFrame: {hex(self.ref)}\n' + \ self._get_feature_description() + \ f' * prev_frame: {hex(self.interp_frame["prev_frame"])}\n' + \ f' * function: {hex(self.interp_frame["function"])}\n' + \ f' * ip: {hex(self.interp_frame["ip"])}\n' + \ self._get_conditional_info() + \ f'{ENDC}\n' class WASMExecEnv: def __init__(self, ref): self.ref = ref self.exec_env = ref.cast( gdb.lookup_type('wasm_exec_env_t') ).dereference() self.prev = self.exec_env['prev'] self.next = self.exec_env['next'] self.interp_mode = 'classic' self.hw_bound_check = False self.gc_enabled = False self.fastjit_enabled = False self.thread_mgr = False self.aot_enabled = False self.source_debugger = False self.wasm_stack_size = int(self.exec_env['wasm_stack_size']) self.thread = hex(int(self.exec_env['handle'])) self.suspend_flags = hex(int(self.exec_env['suspend_flags']['flags'])) cur_frame_val = self.exec_env['cur_frame'] if (cur_frame_val != 0): self.current_frame = WASMInterpFrame(cur_frame_val) else: self.current_frame = None # mode detection exec_env_fields = [field.name for field in self.exec_env.type.fields()] if ('block_addr_cache' not in exec_env_fields): self.interp_mode = 'fast' if ('jmpbuf_stack_top' in exec_env_fields): self.hw_bound_check = True if ('cur_local_object_ref' in exec_env_fields): self.gc_enabled = True if ('jit_cache' in exec_env_fields): self.fastjit_enabled = True if ('cluster' in exec_env_fields): self.thread_mgr = True if ('argv_buf' in exec_env_fields): self.aot_enabled = True if ('current_status' in exec_env_fields): self.source_debugger = True def get_cur_frame(self): return self.current_frame def _get_dynamic_info(self): frame_num = 0 info = '' cur_frame = self.current_frame while (cur_frame): frame_num += 1 cur_frame = cur_frame.get_prev_frame() info += f' * frame_count: {frame_num}\n' return info def _get_feature_description(self): features = ' * features:\n' features += f' - interp_mode: {self.interp_mode}\n' features += f' - hw_bound_check: {"on" if self.hw_bound_check else "off"}\n' features += f' - gc: {"on" if self.gc_enabled else "off"}\n' features += f' - fastjit: {"on" if self.fastjit_enabled else "off"}\n' features += f' - thread_mgr: {"on" if self.thread_mgr else "off"}\n' features += f' - aot: {"on" if self.aot_enabled else "off"}\n' features += f' - source_debugger: {"on" if self.source_debugger else "off"}\n' return features def __str__(self) -> str: return f'{GREEN}WASMExecEnv: {hex(self.ref)}\n' + \ f' * prev: {hex(self.prev)}\n' + \ f' * next: {hex(self.next)}\n' + \ f' * wasm_stack_size: {self.wasm_stack_size}\n' + \ f' * thread: {self.thread}\n' + \ f' * suspend_flags: {self.suspend_flags}\n' + \ f' * current_frame: {self.current_frame.ref if self.current_frame else "null"}\n' + \ self._get_dynamic_info() + \ self._get_feature_description() + \ f'{ENDC}\n' ================================================ FILE: scripts/gdb/wamr_objects.py ================================================ # # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # import gdb from utils import * from wamr_types import * class HMU: def __init__(self, hmu): self.hmu = hmu self.hmu_header_val = gdb.Value(hmu).cast( gdb.lookup_type(HMU_HEADER_TYPE).pointer()).dereference() self.hmu_size = hmu_get_size(self.hmu_header_val) self.hmu_ut = hmu_get_ut(self.hmu_header_val) self.hmu_pinuse = hmu_get_pinuse(self.hmu_header_val) self.hmu_vo_marked = hmu_get_vo_marked(self.hmu_header_val) def __str__(self): return f'{BLUE}Heap Management Unit: {hex(self.hmu)}\n' + \ f' * hmu size: {self.hmu_size}\n' + \ f' * hmu ut: {self.hmu_ut}\n' + \ f' * pinuse: {"true" if self.hmu_pinuse else "false"}\n' + \ f' * marked: {"true" if self.hmu_vo_marked else "false"}\n{ENDC}' class WASMRtt: def __init__(self, rtt_ref): self.rtt_ref = rtt_ref if ((int(rtt_ref) & WASM_OBJ_EXTERNREF_OBJ_FLAG) == WASM_OBJ_EXTERNREF_OBJ_FLAG): self.type_flag = 'WASM_TYPE_EXTERNREF' elif ((int(rtt_ref) & WASM_OBJ_ANYREF_OBJ_FLAG) == WASM_OBJ_ANYREF_OBJ_FLAG): self.type_flag = 'WASM_TYPE_ANYREF' else: self.rtt = rtt_ref.dereference() self.type_flag = TYPE_KIND[self.rtt["type_flag"]] self.inherit_depth = self.rtt["inherit_depth"] def _get_description(self): if (self.type_flag == 'WASM_TYPE_EXTERNREF'): return f'{YELLOW}Runtime Type: {hex(self.rtt_ref)} (externref){ENDC}\n' elif (self.type_flag == 'WASM_TYPE_ANYREF'): return f'{YELLOW}Runtime Type: {hex(self.rtt_ref)} (anyref){ENDC}\n' else: return f'{YELLOW}Runtime Type: {hex(self.rtt_ref)}\n' + \ f' * flag: {self.type_flag}\n' + \ f' * inherit_depth: {self.inherit_depth}\n{ENDC}' def __str__(self): return self._get_description() class WASMObj: def __init__(self, obj, hmu, rtt): self.obj = obj self.hmu = hmu self.rtt = rtt @staticmethod def create_obj(obj_ref): if (get_bit(int(obj_ref))): return WASMI31Obj(int(obj_ref) >> 1) else: try: hmu = HMU(obj_to_hmu(obj_ref)) rtt = WASMRtt(obj_get_rtt_ref(obj_ref)) type_flag = rtt.type_flag if type_flag == 'WASM_TYPE_ARRAY': return WASMArrayObj(obj_ref, hmu, rtt) elif type_flag == 'WASM_TYPE_STRUCT': return WASMStructObj(obj_ref, hmu, rtt) elif type_flag == 'WASM_TYPE_FUNC': return WASMFuncObj(obj_ref, hmu, rtt) elif type_flag == 'WASM_TYPE_EXTERNREF': return WASMExternRefObj(obj_ref, hmu, rtt) elif type_flag == 'WASM_TYPE_ANYREF': return WASMAnyRefObj(obj_ref, hmu, rtt) else: return None except gdb.MemoryError as e: print(f'{RED}Not a WasmGC object{ENDC}') return None class WASMI31Obj: def __init__(self, value): self.value = value def __str__(self): return f'{GREEN}WASM I31: {self.value} ({hex(self.value)}){ENDC}' class WASMExternRefObj(WASMObj): def __init__(self, obj, hmu, rtt): super().__init__(obj, hmu, rtt) self.ref = obj self.extern_ref_obj = obj.cast( gdb.lookup_type('wasm_externref_obj_t') ).dereference() def __str__(self): return f'{PURPLE}WASMExternRefObj: {hex(self.ref)}\n' + \ f' * internal_obj: {hex(self.extern_ref_obj["internal_obj"])}\n{ENDC}' + \ str(self.hmu) + str(self.rtt) class WASMAnyRefObj(WASMObj): def __init__(self, obj, hmu, rtt): super().__init__(obj, hmu, rtt) self.ref = obj self.anyref_obj = obj.cast( gdb.lookup_type('wasm_anyref_obj_t') ).dereference() def __str__(self): return f'{PURPLE}WASMAnyRefObj: {hex(self.ref)}\n' + \ f' * host_ptr: {self.anyref_obj["host_obj"]}\n{ENDC}' + \ str(self.hmu) + str(self.rtt) class WASMArrayObj(WASMObj): def __init__(self, obj, hmu, rtt): super().__init__(obj, hmu, rtt) self.ref = obj self.array_obj = obj.cast( gdb.lookup_type('wasm_array_obj_t') ).dereference() self.array_type = WASMType.create_type( rtt_get_defined_type_ref(rtt.rtt).cast( gdb.lookup_type('wasm_array_type_t') ) ) def __str__(self): return f'{PURPLE}WASMArrayObj: {hex(self.ref)}\n' + \ f' * array length: {self.array_obj["length"] >> WASM_ARRAY_LENGTH_SHIFT}\n' + \ f' * elem size: {1 << (self.array_obj["length"] & WASM_ARRAY_ELEM_SIZE_MASK)}\n{ENDC}' + \ str(self.array_type) + str(self.hmu) + str(self.rtt) class WASMStructObj(WASMObj): def __init__(self, obj, hmu, rtt): super().__init__(obj, hmu, rtt) self.ref = obj self.struct_obj = obj.cast( gdb.lookup_type('wasm_struct_obj_t') ).dereference() self.struct_type = WASMType.create_type( rtt_get_defined_type_ref(rtt.rtt).cast( gdb.lookup_type('wasm_struct_type_t') ) ) def __str__(self): return f'{PURPLE}WASMStructObj: {hex(self.ref)}\n' + \ f'{ENDC}' + \ str(self.struct_type) + str(self.hmu) + str(self.rtt) class WASMFuncObj(WASMObj): def __init__(self, obj, hmu, rtt): super().__init__(obj, hmu, rtt) self.ref = obj self.func_obj = obj.cast( gdb.lookup_type('wasm_func_obj_t') ).dereference() self.func_type = WASMType.create_type( rtt_get_defined_type_ref(rtt.rtt).cast( gdb.lookup_type('wasm_func_type_t') ) ) def __str__(self): return f'{PURPLE}WASMFuncObj: {hex(self.ref)}\n' + \ f' * bound func: {self.func_obj["func_idx_bound"]}\n' + \ f'{ENDC}' + \ str(self.func_type) + str(self.hmu) + str(self.rtt) ================================================ FILE: scripts/gdb/wamr_types.py ================================================ # # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # import gdb from constants import * class WASMType: def __init__(self): pass @staticmethod def create_type(ref): type = ref.cast( gdb.lookup_type('WASMType').pointer() ).dereference() try: type_flag = TYPE_KIND[int(type['type_flag'])] if type_flag == 'WASM_TYPE_ARRAY': return WASMArrayType(ref) elif type_flag == 'WASM_TYPE_STRUCT': return WASMStructType(ref) elif type_flag == 'WASM_TYPE_FUNC': return WASMFuncType(ref) else: return None except (gdb.MemoryError, IndexError) as e: print(f'{RED}Not a WASMType{ENDC}') return None class WASMArrayType(WASMType): def __init__(self, ref): super().__init__() self.ref = ref self.type = ref.cast( gdb.lookup_type('WASMArrayType').pointer() ).dereference() def __str__(self): return f'{GREEN}WASMArrayType: {hex(self.ref)}\n' + \ f' * elem type: {WASM_TYPE_MAP[int(self.type["elem_type"])]}{ENDC}\n' class WASMStructType(WASMType): def __init__(self, ref): super().__init__() self.ref = ref self.type = ref.cast( gdb.lookup_type('WASMStructType').pointer() ).dereference() def _get_fields_info(self): fields_info = [] for i in range(self.type["field_count"]): field_type = self.type["fields"][i] field_info = { 'flag': int(field_type['field_flags']), 'type': WASM_TYPE_MAP[int(field_type['field_type'])], 'size': int(field_type['field_size']), 'offset': int(field_type['field_offset']), } # for ref ht type, get its ref type if field_info['type'] == 'REF_TYPE_HT_NON_NULLABLE' \ or field_info['type'] == 'REF_TYPE_HT_NULLABLE': ref_type_map = self.type['ref_type_maps'] ret_type_count = int(self.type['ref_type_map_count']) for j in range(ret_type_count): ref_type = ref_type_map[j] if (ref_type['index'] == i): field_info['type'] += \ f" (typeid={int(ref_type['ref_type']['ref_ht_typeidx']['type_idx'])})" fields_info.append(field_info) return fields_info def _create_fields_description(self): fields_info = self._get_fields_info() description = '' index = 0 for field in fields_info: description += f' #[{index}] type:\t{field["type"]}\n' description += f'\t mut:\t{"true" if field["flag"] & 1 else "false"}\n' description += f'\t offset:\t{field["offset"]}\n' description += f'\t size:\t{field["size"]}\n' index += 1 return description def __str__(self): return f'{GREEN}WASMStructType: {hex(self.ref)}\n' + \ f' * parent type: {self.type["base_type"]["parent_type_idx"]}\n' + \ f' * field count: {self.type["field_count"]}\n' + \ f' * fields info:\n' + \ self._create_fields_description() + \ f'{ENDC}' class WASMFuncType(WASMType): def __init__(self, ref): super().__init__() self.ref = ref self.type = ref.cast( gdb.lookup_type('WASMFuncType').pointer() ).dereference() self.param_count = int(self.type["param_count"]) self.result_count = int(self.type["result_count"]) def _get_reftype_id(self, i): ref_type_map = self.type['ref_type_maps'] ret_type_count = int(self.type['ref_type_map_count']) for j in range(ret_type_count): ref_type = ref_type_map[j] if (ref_type['index'] == i): return int(ref_type['ref_type']['ref_ht_typeidx']['type_idx']) def _get_parameter_types(self): types = [] for i in range(self.param_count): type = WASM_TYPE_MAP[int(self.type["types"][i])] # for ref ht type, get its ref type if type == 'REF_TYPE_HT_NON_NULLABLE' \ or type == 'REF_TYPE_HT_NULLABLE': type += f' (typeid={self._get_reftype_id(i)})' types.append(type) return types def _get_result_types(self): types = [] for i in range(self.param_count, self.param_count + self.result_count): type = WASM_TYPE_MAP[int(self.type["types"][i])] # for ref ht type, get its ref type if type == 'REF_TYPE_HT_NON_NULLABLE' \ or type == 'REF_TYPE_HT_NULLABLE': type += f' (typeid={self._get_reftype_id(i)})' types.append(type) return types def _get_description(self, results = False): types = self._get_result_types() if results else self._get_parameter_types() description = '' index = 0 for type in types: description += f' #[{index}]\t{type}\n' index += 1 return description def __str__(self): return f'{GREEN}WASMFuncType: {hex(self.ref)}\n' + \ f' * param count: {self.type["param_count"]}\n' + \ f' * result count: {self.type["result_count"]}\n' + \ f' * parameters:\n' + \ self._get_description() + \ f' * results:\n' + \ self._get_description(results=True) + \ f'{ENDC}' ================================================ FILE: scripts/hooks/pre-commit ================================================ npx lint-staged ================================================ FILE: scripts/link_hooks.js ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; const hooks = ['pre-commit']; /** * Create symbolic links for git hooks, * Based on: * https://github.com/microsoft/TypeScript/blob/main/scripts/link-hooks.mjs */ for (const hook of hooks) { const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const hookInSourceControl = path.resolve(__dirname, 'hooks', hook); if (fs.existsSync(hookInSourceControl)) { const gitHookDirectory = path.resolve(__dirname, '..', '.git', 'hooks'); const hookInHiddenDirectory = path.resolve(gitHookDirectory, hook); if (!fs.existsSync(gitHookDirectory)) { continue; } if (fs.existsSync(hookInHiddenDirectory)) { fs.unlinkSync(hookInHiddenDirectory); } fs.linkSync(hookInSourceControl, hookInHiddenDirectory); } } ================================================ FILE: security.md ================================================ # Security Policy Intel is committed to rapidly addressing security vulnerabilities affecting our customers and providing clear guidance on the solution, impact, severity and mitigation. ## Reporting a Vulnerability Please report any security vulnerabilities in this project utilizing the guidelines [here](https://www.intel.com/content/www/us/en/security-center/vulnerability-handling-guidelines.html). ================================================ FILE: src/backend/README.md ================================================ # ts2wasm backend ts2wasm is designed to support multiple backends, every backend should inherit the `Ts2wasmBackend` class defined in index.ts. The backend class can access the `ParserContext` to get all necessary information. ``` TypeScript /* custom_backend.ts */ class CustomBackend extends Ts2wasmBackend { // ... } /* cli.ts */ const parserCtx = new ParserContext(); parserCtx.parse(); const backend = new CustomBackend(parserCtx); backend.codegen(); backend.emitBinary(); ``` ================================================ FILE: src/backend/binaryen/glue/LICENSE ================================================ 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 APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. 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. ================================================ FILE: src/backend/binaryen/glue/NOTICE ================================================ The following authors have all licensed their contributions to AssemblyScript under the licensing terms detailed in LICENSE: * Daniel Wirtz * Max Graey * Igor Sbitnev * Norton Wang * Alan Pierce * Palmer * Linus Unnebäck * Joshua Tenner * Nidin Vinayakan <01@01alchemist.com> * Aaron Turner * Willem Wyndham * Bowen Wang * Emil Laine * Stephen Paul Weber * Jay Phelps * jhwgh1968 * Jeffrey Charles * Vladimir Tikhonov * Duncan Uszkay * Surma * Julien Letellier * Guido Zuidhof * ncave <777696+ncave@users.noreply.github.com> * Andrew Davis * Maël Nison * Valeria Viana Gusmao * Gabor Greif * Martin Fredriksson * forcepusher * Piotr Oleś * Saúl Cabrera * Chance Snow * Peter Salomonsen * ookangzheng * yjhmelody * bnbarak * Colin Eberhardt * Ryan Pivovar * Roman F. <70765447+romdotdog@users.noreply.github.com> * Joe Pea * Felipe Gasper * Congcong Cai * mooooooi * Yasushi Ando * Syed Jafri * Peter Hayman * ApsarasX * Adrien Zinger * Ruixiang Chen * Daniel Salvadori * Jairus Tanaka * CountBleck * Abdul Rauf * Bach Le Portions of this software are derived from third-party works licensed under the following terms: * TypeScript: https://github.com/Microsoft/TypeScript Copyright (c) Microsoft Corporation Apache License, Version 2.0 (https://opensource.org/licenses/Apache-2.0) * Binaryen: https://github.com/WebAssembly/binaryen Copyright (c) WebAssembly Community Group participants Apache License, Version 2.0 (https://opensource.org/licenses/Apache-2.0) * musl libc: http://www.musl-libc.org Copyright (c) Rich Felker, et al. The MIT License (https://opensource.org/licenses/MIT) * V8: https://developers.google.com/v8/ Copyright (c) the V8 project authors The 3-Clause BSD License (https://opensource.org/licenses/BSD-3-Clause) * Arm Optimized Routines: https://github.com/ARM-software/optimized-routines Copyright (c) Arm Limited The MIT License (https://opensource.org/licenses/MIT) ================================================ FILE: src/backend/binaryen/glue/binaryen.d.ts ================================================ /** * @fileoverview Portable definitions for Binaryen's C-API. * * tsc uses the .js file next to it, while asc makes it a Wasm import. * * See: https://github.com/WebAssembly/binaryen/blob/main/src/binaryen-c.h * * @license Apache-2.0 */ /* This file is modified base on: * https://github.com/AssemblyScript/assemblyscript/blob/main/src/glue/binaryen.d.ts * Please see original LICENSE and NOTICE under the same folder */ module 'binaryen'; export declare type bool = boolean; export declare type i8 = number; export declare type i16 = number; export declare type i32 = number; export declare type isize = number; export declare type u8 = number; export declare type u16 = number; export declare type u32 = number; export declare type usize = number; export declare type f32 = number; export declare type f64 = number; type Ref = usize; export type Index = u32; export type ExpressionId = i32; export type FeatureFlags = u32; export type Op = i32; export type ExternalKind = u32; export type SideEffects = u32; export type ExpressionRunnerFlags = u32; export type StringRef = Ref; export type Pointer = Ref; export type ArrayRef = Ref; export type TypeRef = Ref; export type HeapTypeRef = Ref; export type PackedType = u32; export type ModuleRef = Ref; export type LiteralRef = Ref; export type ExpressionRef = Ref; export type FunctionRef = Ref; export type ImportRef = Ref; export type ExportRef = Ref; export type GlobalRef = Ref; export type TagRef = Ref; export type TableRef = Ref; export type ElementSegmentRef = Ref; export type RelooperRef = Ref; export type RelooperBlockRef = Ref; export type ExpressionRunnerRef = Ref; export type BinaryenModuleAllocateAndWriteResultRef = Ref; export type TypeBuilderRef = Ref; export type TypeBuilderErrorReason = u32; export type TypeSystem = u32; export declare function _BinaryenTypeCreate( types: ArrayRef, numTypes: u32, ): TypeRef; export declare function _BinaryenTypeArity(type: TypeRef): u32; export declare function _BinaryenTypeExpand( type: TypeRef, typesOut: ArrayRef, ): void; export declare function _BinaryenTypeGetHeapType(type: TypeRef): HeapTypeRef; export declare function _BinaryenTypeFromHeapType( heapType: HeapTypeRef, nullable: bool, ): TypeRef; export declare function _BinaryenTypeIsNullable(type: TypeRef): bool; export declare function _BinaryenTypeFuncref(): TypeRef; export declare function _BinaryenTypeExternref(): TypeRef; export declare function _BinaryenTypeAnyref(): TypeRef; export declare function _BinaryenTypeEqref(): TypeRef; export declare function _BinaryenTypeStructref(): TypeRef; export declare function _BinaryenTypeArrayref(): TypeRef; export declare function _BinaryenTypeI31ref(): TypeRef; export declare function _BinaryenTypeStringref(): TypeRef; export declare function _BinaryenTypeStringviewWTF8(): TypeRef; export declare function _BinaryenTypeStringviewWTF16(): TypeRef; export declare function _BinaryenTypeStringviewIter(): TypeRef; export declare function _BinaryenTypeNullref(): TypeRef; export declare function _BinaryenTypeNullExternref(): TypeRef; export declare function _BinaryenTypeNullFuncref(): TypeRef; export declare function _BinaryenHeapTypeFunc(): HeapTypeRef; export declare function _BinaryenHeapTypeExt(): HeapTypeRef; export declare function _BinaryenHeapTypeAny(): HeapTypeRef; export declare function _BinaryenHeapTypeEq(): HeapTypeRef; export declare function _BinaryenHeapTypeI31(): HeapTypeRef; export declare function _BinaryenHeapTypeStruct(): HeapTypeRef; export declare function _BinaryenHeapTypeArray(): HeapTypeRef; export declare function _BinaryenHeapTypeString(): HeapTypeRef; export declare function _BinaryenHeapTypeStringviewWTF8(): HeapTypeRef; export declare function _BinaryenHeapTypeStringviewWTF16(): HeapTypeRef; export declare function _BinaryenHeapTypeStringviewIter(): HeapTypeRef; export declare function _BinaryenHeapTypeNone(): HeapTypeRef; export declare function _BinaryenHeapTypeNoext(): HeapTypeRef; export declare function _BinaryenHeapTypeNofunc(): HeapTypeRef; export declare function _BinaryenHeapTypeIsBasic(heapType: HeapTypeRef): bool; export declare function _BinaryenHeapTypeIsSignature( heapType: HeapTypeRef, ): bool; export declare function _BinaryenHeapTypeIsStruct(heapType: HeapTypeRef): bool; export declare function _BinaryenHeapTypeIsArray(heapType: HeapTypeRef): bool; export declare function _BinaryenHeapTypeIsBottom(heapType: HeapTypeRef): bool; export declare function _BinaryenHeapTypeGetBottom( heapType: HeapTypeRef, ): HeapTypeRef; export declare function _BinaryenHeapTypeIsSubType( left: HeapTypeRef, right: HeapTypeRef, ): bool; export declare function _BinaryenStructTypeGetNumFields( heapType: HeapTypeRef, ): Index; export declare function _BinaryenStructTypeGetFieldType( heapType: HeapTypeRef, index: Index, ): TypeRef; export declare function _BinaryenStructTypeGetFieldPackedType( heapType: HeapTypeRef, index: Index, ): PackedType; export declare function _BinaryenStructTypeIsFieldMutable( heapType: HeapTypeRef, index: Index, ): bool; export declare function _BinaryenArrayTypeGetElementType( heapType: HeapTypeRef, ): TypeRef; export declare function _BinaryenArrayTypeGetElementPackedType( heapType: HeapTypeRef, ): PackedType; export declare function _BinaryenArrayTypeIsElementMutable( heapType: HeapTypeRef, ): bool; export declare function _BinaryenSignatureTypeGetParams( heapType: HeapTypeRef, ): TypeRef; export declare function _BinaryenSignatureTypeGetResults( heapType: HeapTypeRef, ): TypeRef; export declare function _BinaryenModuleCreate(): ModuleRef; export declare function _BinaryenModuleDispose(module: ModuleRef): void; export declare function _BinaryenSizeofLiteral(): usize; export declare function _BinaryenLiteralInt32( literalOut: LiteralRef, x: i32, ): void; export declare function _BinaryenLiteralInt64( literalOut: LiteralRef, x: i32, y: i32, ): void; export declare function _BinaryenLiteralFloat32( literalOut: LiteralRef, x: f32, ): void; export declare function _BinaryenLiteralFloat64( literalOut: LiteralRef, x: f64, ): void; export declare function _BinaryenLiteralVec128( literalOut: LiteralRef, x: ArrayRef, ): void; export declare function _BinaryenLiteralFloat32Bits( literalOut: LiteralRef, x: i32, ): void; export declare function _BinaryenLiteralFloat64Bits( literalOut: LiteralRef, x: i32, y: i32, ): void; export declare function _BinaryenExpressionGetId( expr: ExpressionRef, ): ExpressionId; export declare function _BinaryenExpressionGetType( expr: ExpressionRef, ): TypeRef; export declare function _BinaryenExpressionSetType( expr: ExpressionRef, type: TypeRef, ): void; export declare function _BinaryenExpressionPrint(expr: ExpressionRef): void; export declare function _BinaryenExpressionCopy( expr: ExpressionRef, module: ModuleRef, ): ExpressionRef; export declare function _BinaryenExpressionFinalize(expr: ExpressionRef): void; export declare function _BinaryenBlock( module: ModuleRef, name: StringRef, childExprs: ArrayRef, numChildren: Index, type: TypeRef, ): ExpressionRef; export declare function _BinaryenBlockGetName(expr: ExpressionRef): StringRef; export declare function _BinaryenBlockSetName( expr: ExpressionRef, name: StringRef, ): void; export declare function _BinaryenBlockGetNumChildren( expr: ExpressionRef, ): Index; export declare function _BinaryenBlockGetChildAt( expr: ExpressionRef, index: Index, ): ExpressionRef; export declare function _BinaryenBlockSetChildAt( expr: ExpressionRef, index: Index, childExpr: ExpressionRef, ): void; export declare function _BinaryenBlockAppendChild( expr: ExpressionRef, childExpr: ExpressionRef, ): Index; export declare function _BinaryenBlockInsertChildAt( expr: ExpressionRef, index: Index, childExpr: ExpressionRef, ): void; export declare function _BinaryenBlockRemoveChildAt( expr: ExpressionRef, index: Index, ): ExpressionRef; export declare function _BinaryenIf( module: ModuleRef, conditionExpr: ExpressionRef, ifTrueExpr: ExpressionRef, ifFalseExpr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenIfGetCondition( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenIfSetCondition( expr: ExpressionRef, conditionExpr: ExpressionRef, ): void; export declare function _BinaryenIfGetIfTrue( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenIfSetIfTrue( expr: ExpressionRef, ifTrueExpr: ExpressionRef, ): void; export declare function _BinaryenIfGetIfFalse( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenIfSetIfFalse( expr: ExpressionRef, ifFalseExpr: ExpressionRef, ): void; export declare function _BinaryenLoop( module: ModuleRef, name: StringRef, bodyExpr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenLoopGetName(expr: ExpressionRef): StringRef; export declare function _BinaryenLoopSetName( expr: ExpressionRef, name: StringRef, ): void; export declare function _BinaryenLoopGetBody( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenLoopSetBody( expr: ExpressionRef, bodyExpr: ExpressionRef, ): void; export declare function _BinaryenBreak( module: ModuleRef, name: StringRef, conditionExpr: ExpressionRef, valueExpr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenBreakGetName(expr: ExpressionRef): StringRef; export declare function _BinaryenBreakSetName( expr: ExpressionRef, name: StringRef, ): void; export declare function _BinaryenBreakGetCondition( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenBreakSetCondition( expr: ExpressionRef, conditionExpr: ExpressionRef, ): void; export declare function _BinaryenBreakGetValue( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenBreakSetValue( expr: ExpressionRef, valueExpr: ExpressionRef, ): void; export declare function _BinaryenSwitch( module: ModuleRef, names: ArrayRef, numNames: Index, defaultName: StringRef, conditionExpr: ExpressionRef, valueExpr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenSwitchGetNumNames(expr: ExpressionRef): Index; export declare function _BinaryenSwitchGetNameAt( expr: ExpressionRef, index: Index, ): StringRef; export declare function _BinaryenSwitchSetNameAt( expr: ExpressionRef, index: Index, name: StringRef, ): void; export declare function _BinaryenSwitchAppendName( expr: ExpressionRef, name: StringRef, ): Index; export declare function _BinaryenSwitchInsertNameAt( expr: ExpressionRef, index: Index, name: StringRef, ): void; export declare function _BinaryenSwitchRemoveNameAt( expr: ExpressionRef, index: Index, ): StringRef; export declare function _BinaryenSwitchGetDefaultName( expr: ExpressionRef, ): StringRef; export declare function _BinaryenSwitchSetDefaultName( expr: ExpressionRef, defaultName: StringRef, ): void; export declare function _BinaryenSwitchGetCondition( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenSwitchSetCondition( expr: ExpressionRef, conditionExpr: ExpressionRef, ): void; export declare function _BinaryenSwitchGetValue( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenSwitchSetValue( expr: ExpressionRef, valueExpr: ExpressionRef, ): void; export declare function _BinaryenCall( module: ModuleRef, targetName: StringRef, operandExprs: ArrayRef, numOperands: Index, returnType: TypeRef, ): ExpressionRef; export declare function _BinaryenCallGetTarget(expr: ExpressionRef): StringRef; export declare function _BinaryenCallSetTarget( expr: ExpressionRef, targetName: StringRef, ): void; export declare function _BinaryenCallGetNumOperands(expr: ExpressionRef): Index; export declare function _BinaryenCallGetOperandAt( expr: ExpressionRef, index: Index, ): ExpressionRef; export declare function _BinaryenCallSetOperandAt( expr: ExpressionRef, index: Index, operandExpr: ExpressionRef, ): void; export declare function _BinaryenCallAppendOperand( expr: ExpressionRef, operandExpr: ExpressionRef, ): Index; export declare function _BinaryenCallInsertOperandAt( expr: ExpressionRef, index: Index, operandExpr: ExpressionRef, ): void; export declare function _BinaryenCallRemoveOperandAt( expr: ExpressionRef, index: Index, ): ExpressionRef; export declare function _BinaryenCallIsReturn(expr: ExpressionRef): bool; export declare function _BinaryenCallSetReturn( expr: ExpressionRef, isReturn: bool, ): void; // ^ with return = true export declare function _BinaryenReturnCall( module: ModuleRef, targetName: StringRef, operandExprs: ArrayRef, numOperands: Index, returnType: TypeRef, ): ExpressionRef; export declare function _BinaryenCallIndirect( module: ModuleRef, table: StringRef, targetExpr: ExpressionRef, operandExprs: ArrayRef, numOperands: Index, params: TypeRef, results: TypeRef, ): ExpressionRef; export declare function _BinaryenCallIndirectGetTable( expr: ExpressionRef, ): StringRef; export declare function _BinaryenCallIndirectSetTable( expr: ExpressionRef, table: StringRef, ): void; export declare function _BinaryenCallIndirectGetTarget( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenCallIndirectSetTarget( expr: ExpressionRef, targetExpr: ExpressionRef, ): void; export declare function _BinaryenCallIndirectGetNumOperands( expr: ExpressionRef, ): Index; export declare function _BinaryenCallIndirectGetOperandAt( expr: ExpressionRef, index: Index, ): ExpressionRef; export declare function _BinaryenCallIndirectSetOperandAt( expr: ExpressionRef, index: Index, operandExpr: ExpressionRef, ): void; export declare function _BinaryenCallIndirectAppendOperand( expr: ExpressionRef, operandExpr: ExpressionRef, ): Index; export declare function _BinaryenCallIndirectInsertOperandAt( expr: ExpressionRef, index: Index, operandExpr: ExpressionRef, ): void; export declare function _BinaryenCallIndirectRemoveOperandAt( expr: ExpressionRef, index: Index, ): ExpressionRef; export declare function _BinaryenCallIndirectIsReturn( expr: ExpressionRef, ): bool; export declare function _BinaryenCallIndirectSetReturn( expr: ExpressionRef, isReturn: bool, ): void; // ^ with return = true export declare function _BinaryenReturnCallIndirect( module: ModuleRef, table: StringRef, targetExpr: ExpressionRef, operandExprs: ArrayRef, numOperands: Index, params: TypeRef, results: TypeRef, ): ExpressionRef; export declare function _BinaryenLocalGet( module: ModuleRef, index: Index, type: TypeRef, ): ExpressionRef; export declare function _BinaryenLocalGetGetIndex(expr: ExpressionRef): Index; export declare function _BinaryenLocalGetSetIndex( expr: ExpressionRef, index: Index, ): void; export declare function _BinaryenLocalSet( module: ModuleRef, index: Index, valueExpr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenLocalSetIsTee(expr: ExpressionRef): bool; export declare function _BinaryenLocalSetGetIndex(expr: ExpressionRef): Index; export declare function _BinaryenLocalSetSetIndex( expr: ExpressionRef, index: Index, ): void; export declare function _BinaryenLocalSetGetValue( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenLocalSetSetValue( expr: ExpressionRef, valueExpr: ExpressionRef, ): void; // ^ with type != none export declare function _BinaryenLocalTee( module: ModuleRef, index: Index, valueExpr: ExpressionRef, type: TypeRef, ): ExpressionRef; export declare function _BinaryenGlobalGet( module: ModuleRef, name: StringRef, type: TypeRef, ): ExpressionRef; export declare function _BinaryenGlobalGetGetName( expr: ExpressionRef, ): StringRef; export declare function _BinaryenGlobalGetSetName( expr: ExpressionRef, name: StringRef, ): void; export declare function _BinaryenGlobalSet( module: ModuleRef, name: StringRef, value: ExpressionRef, ): ExpressionRef; export declare function _BinaryenGlobalSetGetName( expr: ExpressionRef, ): StringRef; export declare function _BinaryenGlobalSetSetName( expr: ExpressionRef, name: StringRef, ): void; export declare function _BinaryenGlobalSetGetValue( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenGlobalSetSetValue( expr: ExpressionRef, valueExpr: ExpressionRef, ): void; export declare function _BinaryenMemorySize( module: ModuleRef, memoryName: StringRef, memoryIs64: bool, ): ExpressionRef; export declare function _BinaryenMemoryGrow( module: ModuleRef, delta: ExpressionRef, memoryName: StringRef, memoryIs64: bool, ): ExpressionRef; export declare function _BinaryenMemoryGrowGetDelta( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenMemoryGrowSetDelta( expr: ExpressionRef, delta: ExpressionRef, ): void; export declare function _BinaryenLoad( module: ModuleRef, bytes: u32, signed: bool, offset: u32, align: u32, type: TypeRef, ptrExpr: ExpressionRef, memoryName: StringRef, ): ExpressionRef; export declare function _BinaryenLoadIsAtomic(expr: ExpressionRef): bool; export declare function _BinaryenLoadSetAtomic( expr: ExpressionRef, isAtomic: bool, ): void; export declare function _BinaryenLoadIsSigned(expr: ExpressionRef): bool; export declare function _BinaryenLoadSetSigned( expr: ExpressionRef, isSigned: bool, ): void; export declare function _BinaryenLoadGetOffset(expr: ExpressionRef): u32; export declare function _BinaryenLoadSetOffset( expr: ExpressionRef, offset: u32, ): void; export declare function _BinaryenLoadGetBytes(expr: ExpressionRef): u32; export declare function _BinaryenLoadSetBytes( expr: ExpressionRef, bytes: u32, ): void; export declare function _BinaryenLoadGetAlign(expr: ExpressionRef): u32; export declare function _BinaryenLoadSetAlign( expr: ExpressionRef, align: u32, ): void; export declare function _BinaryenLoadGetPtr(expr: ExpressionRef): ExpressionRef; export declare function _BinaryenLoadSetPtr( expr: ExpressionRef, ptrExpr: ExpressionRef, ): void; // ^ with atomic = true export declare function _BinaryenAtomicLoad( module: ModuleRef, bytes: Index, offset: Index, type: TypeRef, ptrExpr: ExpressionRef, memoryName: StringRef, ): ExpressionRef; export declare function _BinaryenStore( module: ModuleRef, bytes: u32, offset: u32, align: u32, ptrExpr: ExpressionRef, valueExpr: ExpressionRef, type: TypeRef, memoryName: StringRef, ): ExpressionRef; export declare function _BinaryenStoreIsAtomic(expr: ExpressionRef): bool; export declare function _BinaryenStoreSetAtomic( expr: ExpressionRef, isAtomic: bool, ): void; export declare function _BinaryenStoreGetBytes(expr: ExpressionRef): u32; export declare function _BinaryenStoreSetBytes( expr: ExpressionRef, bytes: u32, ): void; export declare function _BinaryenStoreGetOffset(expr: ExpressionRef): u32; export declare function _BinaryenStoreSetOffset( expr: ExpressionRef, offset: u32, ): void; export declare function _BinaryenStoreGetAlign(expr: ExpressionRef): u32; export declare function _BinaryenStoreSetAlign( expr: ExpressionRef, align: u32, ): void; export declare function _BinaryenStoreGetPtr( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStoreSetPtr( expr: ExpressionRef, ptrExpr: ExpressionRef, ): void; export declare function _BinaryenStoreGetValue( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStoreSetValue( expr: ExpressionRef, valueExpr: ExpressionRef, ): void; export declare function _BinaryenStoreGetValueType( expr: ExpressionRef, ): TypeRef; export declare function _BinaryenStoreSetValueType( expr: ExpressionRef, valueType: TypeRef, ): void; // ^ with atomic = true export declare function _BinaryenAtomicStore( module: ModuleRef, bytes: Index, offset: Index, ptrExpr: ExpressionRef, valueExpr: ExpressionRef, type: TypeRef, memoryName: StringRef, ): ExpressionRef; export declare function _BinaryenConst( module: ModuleRef, value: LiteralRef, ): ExpressionRef; export declare function _BinaryenConstGetValueI32(expr: ExpressionRef): i32; export declare function _BinaryenConstSetValueI32( expr: ExpressionRef, value: i32, ): void; export declare function _BinaryenConstGetValueI64Low(expr: ExpressionRef): i32; export declare function _BinaryenConstSetValueI64Low( expr: ExpressionRef, value: i32, ): void; export declare function _BinaryenConstGetValueI64High(expr: ExpressionRef): i32; export declare function _BinaryenConstSetValueI64High( expr: ExpressionRef, value: i32, ): void; export declare function _BinaryenConstGetValueF32(expr: ExpressionRef): f32; export declare function _BinaryenConstSetValueF32( expr: ExpressionRef, value: f32, ): void; export declare function _BinaryenConstGetValueF64(expr: ExpressionRef): f64; export declare function _BinaryenConstSetValueF64( expr: ExpressionRef, value: f64, ): void; export declare function _BinaryenConstGetValueV128( expr: ExpressionRef, valueOut: ArrayRef, ): void; export declare function _BinaryenConstSetValueV128( expr: ExpressionRef, value: ArrayRef, ): void; export declare function _BinaryenUnary( module: ModuleRef, op: Op, valueExpr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenUnaryGetOp(expr: ExpressionRef): Op; export declare function _BinaryenUnarySetOp(expr: ExpressionRef, op: Op): void; export declare function _BinaryenUnaryGetValue( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenUnarySetValue( expr: ExpressionRef, valueExpr: ExpressionRef, ): void; export declare function _BinaryenBinary( module: ModuleRef, op: Op, leftExpr: ExpressionRef, rightExpr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenBinaryGetOp(expr: ExpressionRef): Op; export declare function _BinaryenBinarySetOp(expr: ExpressionRef, op: Op): void; export declare function _BinaryenBinaryGetLeft( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenBinarySetLeft( expr: ExpressionRef, leftExpr: ExpressionRef, ): void; export declare function _BinaryenBinaryGetRight( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenBinarySetRight( expr: ExpressionRef, rightExpr: ExpressionRef, ): void; export declare function _BinaryenSelect( module: ModuleRef, conditionExpr: ExpressionRef, ifTrueExpr: ExpressionRef, ifFalseExpr: ExpressionRef, type: TypeRef, ): ExpressionRef; export declare function _BinaryenSelectGetIfTrue( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenSelectSetIfTrue( expr: ExpressionRef, ifTrueExpr: ExpressionRef, ): void; export declare function _BinaryenSelectGetIfFalse( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenSelectSetIfFalse( expr: ExpressionRef, ifFalseExpr: ExpressionRef, ): void; export declare function _BinaryenSelectGetCondition( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenSelectSetCondition( expr: ExpressionRef, conditionExpr: ExpressionRef, ): void; export declare function _BinaryenDrop( module: ModuleRef, valueExpr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenDropGetValue( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenDropSetValue( expr: ExpressionRef, valueExpr: ExpressionRef, ): void; export declare function _BinaryenReturn( module: ModuleRef, valueExpr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenReturnGetValue( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenReturnSetValue( expr: ExpressionRef, valueExpr: ExpressionRef, ): void; export declare function _BinaryenNop(module: ModuleRef): ExpressionRef; export declare function _BinaryenUnreachable(module: ModuleRef): ExpressionRef; export declare function _BinaryenAtomicRMW( module: ModuleRef, op: Op, bytes: u32, offset: u32, ptrExpr: ExpressionRef, valueExpr: ExpressionRef, type: TypeRef, memoryName: StringRef, ): ExpressionRef; export declare function _BinaryenAtomicRMWGetOp(expr: ExpressionRef): Op; export declare function _BinaryenAtomicRMWSetOp( expr: ExpressionRef, op: Op, ): void; export declare function _BinaryenAtomicRMWGetBytes(expr: ExpressionRef): u32; export declare function _BinaryenAtomicRMWSetBytes( expr: ExpressionRef, bytes: u32, ): void; export declare function _BinaryenAtomicRMWGetOffset(expr: ExpressionRef): u32; export declare function _BinaryenAtomicRMWSetOffset( expr: ExpressionRef, offset: u32, ): void; export declare function _BinaryenAtomicRMWGetPtr( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenAtomicRMWSetPtr( expr: ExpressionRef, ptrExpr: ExpressionRef, ): void; export declare function _BinaryenAtomicRMWGetValue( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenAtomicRMWSetValue( expr: ExpressionRef, valueExpr: ExpressionRef, ): void; export declare function _BinaryenAtomicCmpxchg( module: ModuleRef, bytes: u32, offset: u32, ptrExpr: ExpressionRef, expectedExpr: ExpressionRef, replacementExpr: ExpressionRef, type: TypeRef, memoryName: StringRef, ): ExpressionRef; export declare function _BinaryenAtomicCmpxchgGetBytes( expr: ExpressionRef, ): u32; export declare function _BinaryenAtomicCmpxchgSetBytes( expr: ExpressionRef, bytes: u32, ): void; export declare function _BinaryenAtomicCmpxchgGetOffset( expr: ExpressionRef, ): u32; export declare function _BinaryenAtomicCmpxchgSetOffset( expr: ExpressionRef, offset: u32, ): void; export declare function _BinaryenAtomicCmpxchgGetPtr( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenAtomicCmpxchgSetPtr( expr: ExpressionRef, ptrExpr: ExpressionRef, ): void; export declare function _BinaryenAtomicCmpxchgGetExpected( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenAtomicCmpxchgSetExpected( expr: ExpressionRef, expectedExpr: ExpressionRef, ): void; export declare function _BinaryenAtomicCmpxchgGetReplacement( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenAtomicCmpxchgSetReplacement( expr: ExpressionRef, replacementExpr: ExpressionRef, ): void; export declare function _BinaryenAtomicWait( module: ModuleRef, ptrExpr: ExpressionRef, expectedExpr: ExpressionRef, timeoutExpr: ExpressionRef, expectedType: TypeRef, memoryName: StringRef, ): ExpressionRef; export declare function _BinaryenAtomicWaitGetPtr( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenAtomicWaitSetPtr( expr: ExpressionRef, ptrExpr: ExpressionRef, ): void; export declare function _BinaryenAtomicWaitGetExpected( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenAtomicWaitSetExpected( expr: ExpressionRef, expectedExpr: ExpressionRef, ): void; export declare function _BinaryenAtomicWaitGetTimeout( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenAtomicWaitSetTimeout( expr: ExpressionRef, timeoutExpr: ExpressionRef, ): void; export declare function _BinaryenAtomicWaitGetExpectedType( expr: ExpressionRef, ): TypeRef; export declare function _BinaryenAtomicWaitSetExpectedType( expr: ExpressionRef, expectedType: TypeRef, ): void; export declare function _BinaryenAtomicNotify( module: ModuleRef, ptrExpr: ExpressionRef, notifyCountExpr: ExpressionRef, memoryName: StringRef, ): ExpressionRef; export declare function _BinaryenAtomicNotifyGetPtr( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenAtomicNotifySetPtr( expr: ExpressionRef, ptrExpr: ExpressionRef, ): void; export declare function _BinaryenAtomicNotifyGetNotifyCount( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenAtomicNotifySetNotifyCount( expr: ExpressionRef, notifyCountExpr: ExpressionRef, ): void; export declare function _BinaryenAtomicFence( module: ModuleRef, memoryName: StringRef, ): ExpressionRef; export declare function _BinaryenAtomicFenceGetOrder(expr: ExpressionRef): u8; // unused export declare function _BinaryenAtomicFenceSetOrder( expr: ExpressionRef, order: u8, ): void; // unused export declare function _BinaryenSIMDExtract( module: ModuleRef, op: Op, vecExpr: ExpressionRef, index: u8, ): ExpressionRef; export declare function _BinaryenSIMDExtractGetOp(expr: ExpressionRef): Op; export declare function _BinaryenSIMDExtractSetOp( expr: ExpressionRef, op: Op, ): void; export declare function _BinaryenSIMDExtractGetVec( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenSIMDExtractSetVec( expr: ExpressionRef, vecExpr: ExpressionRef, ): void; export declare function _BinaryenSIMDExtractGetIndex(expr: ExpressionRef): u8; export declare function _BinaryenSIMDExtractSetIndex( expr: ExpressionRef, index: u8, ): void; export declare function _BinaryenSIMDReplace( module: ModuleRef, op: Op, vecEpr: ExpressionRef, index: u8, valueExpr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenSIMDReplaceGetOp(expr: ExpressionRef): Op; export declare function _BinaryenSIMDReplaceSetOp( expr: ExpressionRef, op: Op, ): void; export declare function _BinaryenSIMDReplaceGetVec( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenSIMDReplaceSetVec( expr: ExpressionRef, vecExpr: ExpressionRef, ): void; export declare function _BinaryenSIMDReplaceGetIndex(expr: ExpressionRef): u8; export declare function _BinaryenSIMDReplaceSetIndex( expr: ExpressionRef, index: u8, ): void; export declare function _BinaryenSIMDReplaceGetValue( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenSIMDReplaceSetValue( expr: ExpressionRef, valueExpr: ExpressionRef, ): void; export declare function _BinaryenSIMDShuffle( module: ModuleRef, leftExpr: ExpressionRef, rightExpr: ExpressionRef, mask: ArrayRef, ): ExpressionRef; export declare function _BinaryenSIMDShuffleGetLeft( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenSIMDShuffleSetLeft( expr: ExpressionRef, leftExpr: ExpressionRef, ): void; export declare function _BinaryenSIMDShuffleGetRight( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenSIMDShuffleSetRight( expr: ExpressionRef, rightExpr: ExpressionRef, ): void; export declare function _BinaryenSIMDShuffleGetMask( expr: ExpressionRef, maskOut: ArrayRef, ): void; export declare function _BinaryenSIMDShuffleSetMask( expr: ExpressionRef, mask: ArrayRef, ): void; export declare function _BinaryenSIMDTernary( module: ModuleRef, op: Op, aExpr: ExpressionRef, bExpr: ExpressionRef, cExpr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenSIMDTernaryGetOp(expr: ExpressionRef): Op; export declare function _BinaryenSIMDTernarySetOp( expr: ExpressionRef, op: Op, ): void; export declare function _BinaryenSIMDTernaryGetA( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenSIMDTernarySetA( expr: ExpressionRef, aExpr: ExpressionRef, ): void; export declare function _BinaryenSIMDTernaryGetB( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenSIMDTernarySetB( expr: ExpressionRef, bExpr: ExpressionRef, ): void; export declare function _BinaryenSIMDTernaryGetC( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenSIMDTernarySetC( expr: ExpressionRef, cExpr: ExpressionRef, ): void; export declare function _BinaryenSIMDShift( module: ModuleRef, op: Op, vecExpr: ExpressionRef, shiftExpr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenSIMDShiftGetOp(expr: ExpressionRef): Op; export declare function _BinaryenSIMDShiftSetOp( expr: ExpressionRef, op: Op, ): void; export declare function _BinaryenSIMDShiftGetVec( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenSIMDShiftSetVec( expr: ExpressionRef, vecExpr: ExpressionRef, ): void; export declare function _BinaryenSIMDShiftGetShift( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenSIMDShiftSetShift( expr: ExpressionRef, shiftExpr: ExpressionRef, ): void; export declare function _BinaryenSIMDLoad( module: ModuleRef, op: Op, offset: u32, align: u32, ptrExpr: ExpressionRef, memoryName: StringRef, ): ExpressionRef; export declare function _BinaryenSIMDLoadGetOp(expr: ExpressionRef): Op; export declare function _BinaryenSIMDLoadSetOp( expr: ExpressionRef, op: Op, ): void; export declare function _BinaryenSIMDLoadGetOffset(expr: ExpressionRef): u32; export declare function _BinaryenSIMDLoadSetOffset( expr: ExpressionRef, offset: u32, ): void; export declare function _BinaryenSIMDLoadGetAlign(expr: ExpressionRef): u32; export declare function _BinaryenSIMDLoadSetAlign( expr: ExpressionRef, align: u32, ): void; export declare function _BinaryenSIMDLoadGetPtr( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenSIMDLoadSetPtr( expr: ExpressionRef, ptrExpr: ExpressionRef, ): void; export declare function _BinaryenSIMDLoadStoreLane( module: ModuleRef, op: Op, offset: u32, align: u32, index: u8, ptr: ExpressionRef, vec: ExpressionRef, memoryName: StringRef, ): ExpressionRef; export declare function _BinaryenSIMDLoadStoreLaneGetOp( expr: ExpressionRef, ): Op; export declare function _BinaryenSIMDLoadStoreLaneSetOp( expr: ExpressionRef, op: Op, ): void; export declare function _BinaryenSIMDLoadStoreLaneGetOffset( expr: ExpressionRef, ): u32; export declare function _BinaryenSIMDLoadStoreLaneSetOffset( expr: ExpressionRef, offset: u32, ): void; export declare function _BinaryenSIMDLoadStoreLaneGetAlign( expr: ExpressionRef, ): u32; export declare function _BinaryenSIMDLoadStoreLaneSetAlign( expr: ExpressionRef, align: u32, ): void; export declare function _BinaryenSIMDLoadStoreLaneGetIndex( expr: ExpressionRef, ): u8; export declare function _BinaryenSIMDLoadStoreLaneSetIndex( expr: ExpressionRef, index: u8, ): void; export declare function _BinaryenSIMDLoadStoreLaneGetPtr( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenSIMDLoadStoreLaneSetPtr( expr: ExpressionRef, ptrExpr: ExpressionRef, ): void; export declare function _BinaryenSIMDLoadStoreLaneGetVec( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenSIMDLoadStoreLaneSetVec( expr: ExpressionRef, vecExpr: ExpressionRef, ): void; export declare function _BinaryenSIMDLoadStoreLaneIsStore( expr: ExpressionRef, ): bool; export declare function _BinaryenMemoryInit( module: ModuleRef, segmentIndex: u32, destExpr: ExpressionRef, offsetExpr: ExpressionRef, sizeExpr: ExpressionRef, memoryName: StringRef, ): ExpressionRef; export declare function _BinaryenMemoryInitGetSegment(expr: ExpressionRef): u32; export declare function _BinaryenMemoryInitSetSegment( expr: ExpressionRef, segmentIndex: u32, ): void; export declare function _BinaryenMemoryInitGetDest( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenMemoryInitSetDest( expr: ExpressionRef, destExpr: ExpressionRef, ): void; export declare function _BinaryenMemoryInitGetOffset( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenMemoryInitSetOffset( expr: ExpressionRef, offsetExpr: ExpressionRef, ): void; export declare function _BinaryenMemoryInitGetSize( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenMemoryInitSetSize( expr: ExpressionRef, sizeExpr: ExpressionRef, ): void; export declare function _BinaryenDataDrop( module: ModuleRef, segmentIndex: u32, ): ExpressionRef; export declare function _BinaryenDataDropGetSegment(expr: ExpressionRef): u32; export declare function _BinaryenDataDropSetSegment( expr: ExpressionRef, segmentIndex: u32, ): void; export declare function _BinaryenMemoryCopy( module: ModuleRef, destExpr: ExpressionRef, sourceExpr: ExpressionRef, sizeExpr: ExpressionRef, destMemoryName: StringRef, sourceMemoryName: StringRef, ): ExpressionRef; export declare function _BinaryenMemoryCopyGetDest( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenMemoryCopySetDest( expr: ExpressionRef, destExpr: ExpressionRef, ): void; export declare function _BinaryenMemoryCopyGetSource( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenMemoryCopySetSource( expr: ExpressionRef, sourceExpr: ExpressionRef, ): void; export declare function _BinaryenMemoryCopyGetSize( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenMemoryCopySetSize( expr: ExpressionRef, sizeExpr: ExpressionRef, ): void; export declare function _BinaryenMemoryFill( module: ModuleRef, destExpr: ExpressionRef, valueExpr: ExpressionRef, sizeExpr: ExpressionRef, memoryName: StringRef, ): ExpressionRef; export declare function _BinaryenMemoryFillGetDest( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenMemoryFillSetDest( expr: ExpressionRef, destExpr: ExpressionRef, ): void; export declare function _BinaryenMemoryFillGetValue( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenMemoryFillSetValue( expr: ExpressionRef, valueExpr: ExpressionRef, ): void; export declare function _BinaryenMemoryFillGetSize( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenMemoryFillSetSize( expr: ExpressionRef, sizeExpr: ExpressionRef, ): void; export declare function _BinaryenRefNull( module: ModuleRef, type: TypeRef, ): ExpressionRef; export declare function _BinaryenRefIsNull( module: ModuleRef, valueExpr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenRefIsNullGetValue( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenRefIsNullSetValue( expr: ExpressionRef, valueExpr: ExpressionRef, ): void; export declare function _BinaryenRefAs( module: ModuleRef, op: Op, valueExpr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenRefAsGetOp(expr: ExpressionRef): Op; export declare function _BinaryenRefAsSetOp(expr: ExpressionRef, op: Op): void; export declare function _BinaryenRefAsGetValue( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenRefAsSetValue( expr: ExpressionRef, valueExpr: ExpressionRef, ): void; export declare function _BinaryenRefFunc( module: ModuleRef, funcName: StringRef, type: TypeRef, ): ExpressionRef; export declare function _BinaryenRefFuncGetFunc(expr: ExpressionRef): StringRef; export declare function _BinaryenRefFuncSetFunc( expr: ExpressionRef, funcName: StringRef, ): void; export declare function _BinaryenRefEq( module: ModuleRef, leftExpr: ExpressionRef, rightExpr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenRefEqGetLeft( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenRefEqSetLeft( expr: ExpressionRef, leftExpr: ExpressionRef, ): void; export declare function _BinaryenRefEqGetRight( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenRefEqSetRight( expr: ExpressionRef, rightExpr: ExpressionRef, ): void; export declare function _BinaryenTableGet( module: ModuleRef, name: StringRef, index: ExpressionRef, type: TypeRef, ): ExpressionRef; export declare function _BinaryenTableGetGetTable( expr: ExpressionRef, ): StringRef; export declare function _BinaryenTableGetSetTable( expr: ExpressionRef, table: StringRef, ): void; export declare function _BinaryenTableGetGetIndex( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenTableGetSetIndex( expr: ExpressionRef, index: ExpressionRef, ): void; export declare function _BinaryenTableSet( module: ModuleRef, name: StringRef, index: ExpressionRef, value: ExpressionRef, ): ExpressionRef; export declare function _BinaryenTableSetGetTable( expr: ExpressionRef, ): StringRef; export declare function _BinaryenTableSetSetTable( expr: ExpressionRef, table: StringRef, ): void; export declare function _BinaryenTableSetGetIndex( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenTableSetSetIndex( expr: ExpressionRef, index: ExpressionRef, ): void; export declare function _BinaryenTableSetGetValue( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenTableSetSetValue( expr: ExpressionRef, value: ExpressionRef, ): void; export declare function _BinaryenTableSize( module: ModuleRef, name: StringRef, ): ExpressionRef; export declare function _BinaryenTableSizeGetTable( expr: ExpressionRef, ): StringRef; export declare function _BinaryenTableSizeSetTable( expr: ExpressionRef, table: StringRef, ): void; export declare function _BinaryenTableGrow( module: ModuleRef, name: StringRef, value: ExpressionRef, delta: ExpressionRef, ): ExpressionRef; export declare function _BinaryenTableGrowGetTable( expr: ExpressionRef, ): StringRef; export declare function _BinaryenTableGrowSetTable( expr: ExpressionRef, table: StringRef, ): void; export declare function _BinaryenTableGrowGetValue( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenTableGrowSetValue( expr: ExpressionRef, value: ExpressionRef, ): void; export declare function _BinaryenTableGrowGetDelta( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenTableGrowSetDelta( expr: ExpressionRef, delta: ExpressionRef, ): void; export declare function _BinaryenTry( module: ModuleRef, name: StringRef, bodyExpr: ExpressionRef, catchTags: ArrayRef, numCatchTags: Index, catchBodies: ArrayRef, numCatchBodies: Index, delegateTarget: StringRef, ): ExpressionRef; export declare function _BinaryenTryGetName(expr: ExpressionRef): StringRef; export declare function _BinaryenTrySetName( expr: ExpressionRef, name: StringRef, ): void; export declare function _BinaryenTryGetBody(expr: ExpressionRef): ExpressionRef; export declare function _BinaryenTrySetBody( expr: ExpressionRef, bodyExpr: ExpressionRef, ): void; export declare function _BinaryenTryGetNumCatchTags(expr: ExpressionRef): Index; export declare function _BinaryenTryGetNumCatchBodies( expr: ExpressionRef, ): Index; export declare function _BinaryenTryGetCatchTagAt( expr: ExpressionRef, index: Index, ): StringRef; export declare function _BinaryenTrySetCatchTagAt( expr: ExpressionRef, index: Index, catchTag: StringRef, ): void; export declare function _BinaryenTryAppendCatchTag( expr: ExpressionRef, catchTag: StringRef, ): Index; export declare function _BinaryenTryInsertCatchTagAt( expr: ExpressionRef, index: Index, catchTag: StringRef, ): void; export declare function _BinaryenTryRemoveCatchTagAt( expr: ExpressionRef, index: Index, ): StringRef; export declare function _BinaryenTryGetCatchBodyAt( expr: ExpressionRef, index: Index, ): ExpressionRef; export declare function _BinaryenTrySetCatchBodyAt( expr: ExpressionRef, index: Index, catchExpr: ExpressionRef, ): void; export declare function _BinaryenTryAppendCatchBody( expr: ExpressionRef, catchExpr: ExpressionRef, ): Index; export declare function _BinaryenTryInsertCatchBodyAt( expr: ExpressionRef, index: Index, catchExpr: ExpressionRef, ): void; export declare function _BinaryenTryRemoveCatchBodyAt( expr: ExpressionRef, index: Index, ): ExpressionRef; export declare function _BinaryenTryHasCatchAll(expr: ExpressionRef): bool; export declare function _BinaryenTryGetDelegateTarget( expr: ExpressionRef, ): StringRef; export declare function _BinaryenTrySetDelegateTarget( expr: ExpressionRef, delegateTarget: StringRef, ): void; export declare function _BinaryenTryIsDelegate(expr: ExpressionRef): bool; export declare function _BinaryenThrow( module: ModuleRef, tagName: StringRef, operands: ArrayRef, numOperands: Index, ): ExpressionRef; export declare function _BinaryenThrowGetTag(expr: ExpressionRef): StringRef; export declare function _BinaryenThrowSetTag( expr: ExpressionRef, tagName: StringRef, ): void; export declare function _BinaryenThrowGetNumOperands( expr: ExpressionRef, ): Index; export declare function _BinaryenThrowGetOperandAt( expr: ExpressionRef, index: Index, ): ExpressionRef; export declare function _BinaryenThrowSetOperandAt( expr: ExpressionRef, index: Index, operandExpr: ExpressionRef, ): void; export declare function _BinaryenThrowAppendOperand( expr: ExpressionRef, operandExpr: ExpressionRef, ): Index; export declare function _BinaryenThrowInsertOperandAt( expr: ExpressionRef, index: Index, operandExpr: ExpressionRef, ): void; export declare function _BinaryenThrowRemoveOperandAt( expr: ExpressionRef, index: Index, ): ExpressionRef; export declare function _BinaryenRethrow( module: ModuleRef, target: StringRef, ): ExpressionRef; export declare function _BinaryenRethrowGetTarget( expr: ExpressionRef, ): StringRef; export declare function _BinaryenRethrowSetDepth( expr: ExpressionRef, target: StringRef, ): void; export declare function _BinaryenTupleMake( module: ModuleRef, operandExprs: ArrayRef, numOperands: Index, ): ExpressionRef; export declare function _BinaryenTupleMakeGetNumOperands( expr: ExpressionRef, ): Index; export declare function _BinaryenTupleMakeGetOperandAt( expr: ExpressionRef, index: Index, ): ExpressionRef; export declare function _BinaryenTupleMakeSetOperandAt( expr: ExpressionRef, index: Index, operandExpr: ExpressionRef, ): void; export declare function _BinaryenTupleMakeAppendOperand( expr: ExpressionRef, operandExpr: ExpressionRef, ): Index; export declare function _BinaryenTupleMakeInsertOperandAt( expr: ExpressionRef, index: Index, operandExpr: ExpressionRef, ): void; export declare function _BinaryenTupleMakeRemoveOperandAt( expr: ExpressionRef, index: Index, ): ExpressionRef; export declare function _BinaryenTupleExtract( module: ModuleRef, tupleExpr: ExpressionRef, index: Index, ): ExpressionRef; export declare function _BinaryenTupleExtractGetTuple( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenTupleExtractSetTuple( expr: ExpressionRef, tupleExpr: ExpressionRef, ): void; export declare function _BinaryenTupleExtractGetIndex( expr: ExpressionRef, ): Index; export declare function _BinaryenTupleExtractSetIndex( expr: ExpressionRef, index: Index, ): void; export declare function _BinaryenPop( module: ModuleRef, type: TypeRef, ): ExpressionRef; export declare function _BinaryenI31New( module: ModuleRef, value: ExpressionRef, ): ExpressionRef; export declare function _BinaryenI31NewGetValue( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenI31NewSetValue( expr: ExpressionRef, valueExpr: ExpressionRef, ): void; export declare function _BinaryenI31Get( module: ModuleRef, i31Expr: ExpressionRef, signed: bool, ): ExpressionRef; export declare function _BinaryenI31GetGetI31( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenI31GetSetI31( expr: ExpressionRef, i31Expr: ExpressionRef, ): void; export declare function _BinaryenI31GetIsSigned(expr: ExpressionRef): bool; export declare function _BinaryenI31GetSetSigned( expr: ExpressionRef, signed: bool, ): void; export declare function _BinaryenCallRef( module: ModuleRef, target: ExpressionRef, operands: ArrayRef, numOperands: Index, type: TypeRef, isReturn: bool, ): ExpressionRef; export declare function _BinaryenCallRefGetNumOperands( expr: ExpressionRef, ): Index; export declare function _BinaryenCallRefGetOperandAt( expr: ExpressionRef, index: Index, ): ExpressionRef; export declare function _BinaryenCallRefSetOperandAt( expr: ExpressionRef, index: Index, operandExpr: ExpressionRef, ): void; export declare function _BinaryenCallRefAppendOperand( expr: ExpressionRef, operandExpr: ExpressionRef, ): Index; export declare function _BinaryenCallRefInsertOperandAt( expr: ExpressionRef, index: Index, operandExpr: ExpressionRef, ): void; export declare function _BinaryenCallRefRemoveOperandAt( expr: ExpressionRef, index: Index, ): ExpressionRef; export declare function _BinaryenCallRefGetTarget( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenCallRefSetTarget( expr: ExpressionRef, targetExpr: ExpressionRef, ): void; export declare function _BinaryenCallRefIsReturn(expr: ExpressionRef): bool; export declare function _BinaryenCallRefSetReturn( expr: ExpressionRef, isReturn: bool, ): void; export declare function _BinaryenRefTest( module: ModuleRef, refExpr: ExpressionRef, castType: TypeRef, ): ExpressionRef; export declare function _BinaryenRefTestGetRef( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenRefTestSetRef( expr: ExpressionRef, refExpr: ExpressionRef, ): void; export declare function _BinaryenRefTestGetCastType( expr: ExpressionRef, ): HeapTypeRef; export declare function _BinaryenRefTestSetCastType( expr: ExpressionRef, castType: HeapTypeRef, ): void; export declare function _BinaryenRefCast( module: ModuleRef, refExpr: ExpressionRef, intendedType: TypeRef, ): ExpressionRef; export declare function _BinaryenRefCastGetRef( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenRefCastSetRef( expr: ExpressionRef, refExpr: ExpressionRef, ): void; export declare function _BinaryenBrOn( module: ModuleRef, op: Op, name: StringRef, ref: ExpressionRef, castType: HeapTypeRef, ): ExpressionRef; export declare function _BinaryenBrOnGetOp(expr: ExpressionRef): Op; export declare function _BinaryenBrOnSetOp(expr: ExpressionRef, op: Op): void; export declare function _BinaryenBrOnGetName(expr: ExpressionRef): StringRef; export declare function _BinaryenBrOnSetName( expr: ExpressionRef, nameStr: StringRef, ): void; export declare function _BinaryenBrOnGetRef(expr: ExpressionRef): ExpressionRef; export declare function _BinaryenBrOnSetRef( expr: ExpressionRef, refExpr: ExpressionRef, ): void; export declare function _BinaryenBrOnGetCastType( expr: ExpressionRef, ): HeapTypeRef; export declare function _BinaryenBrOnSetCastType( expr: ExpressionRef, castType: HeapTypeRef, ): void; export declare function _BinaryenStructNew( module: ModuleRef, operands: ArrayRef, numOperands: Index, type: HeapTypeRef, ): ExpressionRef; export declare function _BinaryenStructNewGetNumOperands( expr: ExpressionRef, ): Index; export declare function _BinaryenStructNewGetOperandAt( expr: ExpressionRef, index: Index, ): ExpressionRef; export declare function _BinaryenStructNewSetOperandAt( expr: ExpressionRef, index: Index, operandExpr: ExpressionRef, ): void; export declare function _BinaryenStructNewAppendOperand( expr: ExpressionRef, operandExpr: ExpressionRef, ): Index; export declare function _BinaryenStructNewInsertOperandAt( expr: ExpressionRef, index: Index, operandExpr: ExpressionRef, ): void; export declare function _BinaryenStructNewRemoveOperandAt( expr: ExpressionRef, index: Index, ): ExpressionRef; export declare function _BinaryenStructGet( module: ModuleRef, index: Index, ref: ExpressionRef, type: TypeRef, signed: bool, ): ExpressionRef; export declare function _BinaryenStructGetGetIndex(expr: ExpressionRef): Index; export declare function _BinaryenStructGetSetIndex( expr: ExpressionRef, index: Index, ): void; export declare function _BinaryenStructGetGetRef( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStructGetSetRef( expr: ExpressionRef, refExpr: ExpressionRef, ): void; export declare function _BinaryenStructGetIsSigned(expr: ExpressionRef): bool; export declare function _BinaryenStructGetSetSigned( expr: ExpressionRef, signed: bool, ): void; export declare function _BinaryenStructSet( module: ModuleRef, index: Index, ref: ExpressionRef, value: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStructSetGetIndex(expr: ExpressionRef): Index; export declare function _BinaryenStructSetSetIndex( expr: ExpressionRef, index: Index, ): void; export declare function _BinaryenStructSetGetRef( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStructSetSetRef( expr: ExpressionRef, refExpr: ExpressionRef, ): void; export declare function _BinaryenStructSetGetValue( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStructSetSetValue( expr: ExpressionRef, valueExpr: ExpressionRef, ): void; export declare function _BinaryenArrayNew( module: ModuleRef, type: HeapTypeRef, size: ExpressionRef, init: ExpressionRef, ): ExpressionRef; export declare function _BinaryenArrayNewGetInit( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenArrayNewSetInit( expr: ExpressionRef, initExpr: ExpressionRef, ): void; export declare function _BinaryenArrayNewGetSize( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenArrayNewSetSize( expr: ExpressionRef, sizeExpr: ExpressionRef, ): void; // TODO: BinaryenArrayNewSeg export declare function _BinaryenArrayNewFixed( module: ModuleRef, type: HeapTypeRef, values: ArrayRef, numValues: Index, ): ExpressionRef; export declare function _BinaryenArrayNewFixedGetNumValues( expr: ExpressionRef, ): Index; export declare function _BinaryenArrayNewFixedGetValueAt( expr: ExpressionRef, index: Index, ): ExpressionRef; export declare function _BinaryenArrayNewFixedSetValueAt( expr: ExpressionRef, index: Index, valueExpr: ExpressionRef, ): void; export declare function _BinaryenArrayNewFixedAppendValue( expr: ExpressionRef, valueExpr: ExpressionRef, ): Index; export declare function _BinaryenArrayNewFixedInsertValueAt( expr: ExpressionRef, index: Index, valueExpr: ExpressionRef, ): void; export declare function _BinaryenArrayNewFixedRemoveValueAt( expr: ExpressionRef, index: Index, ): ExpressionRef; export declare function _BinaryenArrayGet( module: ModuleRef, ref: ExpressionRef, index: ExpressionRef, type: TypeRef, signed: bool, ): ExpressionRef; export declare function _BinaryenArrayGetGetRef( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenArrayGetSetRef( expr: ExpressionRef, refExpr: ExpressionRef, ): void; export declare function _BinaryenArrayGetGetIndex( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenArrayGetSetIndex( expr: ExpressionRef, indexExpr: ExpressionRef, ): void; export declare function _BinaryenArrayGetIsSigned(expr: ExpressionRef): bool; export declare function _BinaryenArrayGetSetSigned( expr: ExpressionRef, signed: bool, ): void; export declare function _BinaryenArraySet( module: ModuleRef, ref: ExpressionRef, index: ExpressionRef, value: ExpressionRef, ): ExpressionRef; export declare function _BinaryenArraySetGetRef( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenArraySetSetRef( expr: ExpressionRef, refExpr: ExpressionRef, ): void; export declare function _BinaryenArraySetGetIndex( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenArraySetSetIndex( expr: ExpressionRef, indexExpr: ExpressionRef, ): void; export declare function _BinaryenArraySetGetValue( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenArraySetSetValue( expr: ExpressionRef, valueExpr: ExpressionRef, ): void; export declare function _BinaryenArrayLen( module: ModuleRef, ref: ExpressionRef, ): ExpressionRef; export declare function _BinaryenArrayLenGetRef( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenArrayLenSetRef( expr: ExpressionRef, refExpr: ExpressionRef, ): void; export declare function _BinaryenArrayCopy( module: ModuleRef, destRef: ExpressionRef, destIndex: ExpressionRef, srcRef: ExpressionRef, srcIndex: ExpressionRef, length: ExpressionRef, ): ExpressionRef; export declare function _BinaryenArrayCopyGetDestRef( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenArrayCopySetDestRef( expr: ExpressionRef, destRefExpr: ExpressionRef, ): void; export declare function _BinaryenArrayCopyGetDestIndex( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenArrayCopySetDestIndex( expr: ExpressionRef, destIndexExpr: ExpressionRef, ): void; export declare function _BinaryenArrayCopyGetSrcRef( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenArrayCopySetSrcRef( expr: ExpressionRef, srcRefExpr: ExpressionRef, ): void; export declare function _BinaryenArrayCopyGetSrcIndex( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenArrayCopySetSrcIndex( expr: ExpressionRef, srcIndexExpr: ExpressionRef, ): void; export declare function _BinaryenArrayCopyGetLength( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenArrayCopySetLength( expr: ExpressionRef, lengthExpr: ExpressionRef, ): void; export declare function _BinaryenStringNew( module: ModuleRef, op: Op, ptr: ExpressionRef, length: ExpressionRef, start: ExpressionRef, end: ExpressionRef, isTry: bool, ): ExpressionRef; export declare function _BinaryenStringNewGetOp(expr: ExpressionRef): Op; export declare function _BinaryenStringNewSetOp( expr: ExpressionRef, op: Op, ): void; export declare function _BinaryenStringNewGetPtr( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringNewSetPtr( expr: ExpressionRef, ptrExpr: ExpressionRef, ): void; export declare function _BinaryenStringNewGetLength( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringNewSetLength( expr: ExpressionRef, lengthExpr: ExpressionRef, ): void; export declare function _BinaryenStringNewGetStart( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringNewSetStart( expr: ExpressionRef, startExpr: ExpressionRef, ): void; export declare function _BinaryenStringNewGetEnd( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringNewSetEnd( expr: ExpressionRef, endExpr: ExpressionRef, ): void; export declare function _BinaryenStringNewIsTry(expr: ExpressionRef): bool; export declare function _BinaryenStringNewSetTry( expr: ExpressionRef, isTry: bool, ): void; export declare function _BinaryenStringConst( module: ExpressionRef, name: StringRef, ): ExpressionRef; export declare function _BinaryenStringConstGetString( expr: ExpressionRef, ): StringRef; export declare function _BinaryenStringConstSetString( expr: ExpressionRef, string: StringRef, ): void; export declare function _BinaryenStringMeasure( module: ExpressionRef, op: Op, ref: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringMeasureGetOp(expr: ExpressionRef): Op; export declare function _BinaryenStringMeasureSetOp( expr: ExpressionRef, op: Op, ): void; export declare function _BinaryenStringMeasureGetRef( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringMeasureSetRef( expr: ExpressionRef, refExpr: ExpressionRef, ): void; export declare function _BinaryenStringEncode( module: ExpressionRef, op: Op, ref: ExpressionRef, ptr: ExpressionRef, start: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringEncodeGetOp(expr: ExpressionRef): Op; export declare function _BinaryenStringEncodeSetOp( expr: ExpressionRef, op: Op, ): void; export declare function _BinaryenStringEncodeGetRef( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringEncodeSetRef( expr: ExpressionRef, refExpr: ExpressionRef, ): void; export declare function _BinaryenStringEncodeGetPtr( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringEncodeSetPtr( expr: ExpressionRef, ptrExpr: ExpressionRef, ): void; export declare function _BinaryenStringEncodeGetStart( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringEncodeSetStart( expr: ExpressionRef, startExpr: ExpressionRef, ): void; export declare function _BinaryenStringConcat( module: ExpressionRef, left: ExpressionRef, right: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringConcatGetLeft( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringConcatSetLeft( expr: ExpressionRef, leftExpr: ExpressionRef, ): void; export declare function _BinaryenStringConcatGetRight( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringConcatSetRight( expr: ExpressionRef, rightExpr: ExpressionRef, ): void; export declare function _BinaryenStringEq( module: ExpressionRef, op: Op, left: ExpressionRef, right: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringEqGetOp(expr: ExpressionRef): Op; export declare function _BinaryenStringEqSetOp( expr: ExpressionRef, op: Op, ): void; export declare function _BinaryenStringEqGetLeft( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringEqSetLeft( expr: ExpressionRef, leftExpr: ExpressionRef, ): void; export declare function _BinaryenStringEqGetRight( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringEqSetRight( expr: ExpressionRef, rightExpr: ExpressionRef, ): void; export declare function _BinaryenStringAs( module: ExpressionRef, op: Op, ref: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringAsGetOp(expr: ExpressionRef): Op; export declare function _BinaryenStringAsSetOp( expr: ExpressionRef, op: Op, ): void; export declare function _BinaryenStringAsGetRef( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringAsSetRef( expr: ExpressionRef, refExpr: ExpressionRef, ): void; export declare function _BinaryenStringWTF8Advance( module: ExpressionRef, ref: ExpressionRef, pos: ExpressionRef, bytes: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringWTF8AdvanceGetRef( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringWTF8AdvanceSetRef( expr: ExpressionRef, refExpr: ExpressionRef, ): void; export declare function _BinaryenStringWTF8AdvanceGetPos( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringWTF8AdvanceSetPos( expr: ExpressionRef, posExpr: ExpressionRef, ): void; export declare function _BinaryenStringWTF8AdvanceGetBytes( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringWTF8AdvanceSetBytes( expr: ExpressionRef, bytesExpr: ExpressionRef, ): void; export declare function _BinaryenStringWTF16Get( module: ExpressionRef, ref: ExpressionRef, pos: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringWTF16GetGetRef( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringWTF16GetSetRef( expr: ExpressionRef, refExpr: ExpressionRef, ): void; export declare function _BinaryenStringWTF16GetGetPos( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringWTF16GetSetPos( expr: ExpressionRef, posExpr: ExpressionRef, ): void; export declare function _BinaryenStringIterNext( module: ExpressionRef, ref: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringIterNextGetRef( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringIterNextSetRef( expr: ExpressionRef, refExpr: ExpressionRef, ): void; export declare function _BinaryenStringIterMove( module: ExpressionRef, op: Op, ref: ExpressionRef, num: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringIterMoveGetOp(expr: ExpressionRef): Op; export declare function _BinaryenStringIterMoveSetOp( expr: ExpressionRef, op: Op, ): void; export declare function _BinaryenStringIterMoveGetRef( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringIterMoveSetRef( expr: ExpressionRef, refExpr: ExpressionRef, ): void; export declare function _BinaryenStringIterMoveGetNum( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringIterMoveSetNum( expr: ExpressionRef, numExpr: ExpressionRef, ): void; export declare function _BinaryenStringSliceWTF( module: ExpressionRef, op: Op, ref: ExpressionRef, start: ExpressionRef, end: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringSliceWTFGetOp(expr: ExpressionRef): Op; export declare function _BinaryenStringSliceWTFSetOp( expr: ExpressionRef, op: Op, ): void; export declare function _BinaryenStringSliceWTFGetRef( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringSliceWTFSetRef( expr: ExpressionRef, refExpr: ExpressionRef, ): void; export declare function _BinaryenStringSliceWTFGetStart( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringSliceWTFSetStart( expr: ExpressionRef, startExpr: ExpressionRef, ): void; export declare function _BinaryenStringSliceWTFGetEnd( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringSliceWTFSetEnd( expr: ExpressionRef, endExpr: ExpressionRef, ): void; export declare function _BinaryenStringSliceIter( module: ExpressionRef, ref: ExpressionRef, num: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringSliceIterGetRef( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringSliceIterSetRef( expr: ExpressionRef, refExpr: ExpressionRef, ): void; export declare function _BinaryenStringSliceIterGetNum( expr: ExpressionRef, ): ExpressionRef; export declare function _BinaryenStringSliceIterSetNum( expr: ExpressionRef, numExpr: ExpressionRef, ): void; export declare function _BinaryenAddFunction( module: ModuleRef, name: StringRef, params: TypeRef, results: TypeRef, varTypes: ArrayRef, numVarTypes: Index, body: ExpressionRef, ): FunctionRef; export declare function _BinaryenAddFunctionWithHeapType( module: ModuleRef, name: StringRef, type: HeapTypeRef, varTypes: ArrayRef, numVarTypes: Index, body: ExpressionRef, ): FunctionRef; export declare function _BinaryenGetFunction( module: ModuleRef, name: StringRef, ): FunctionRef; export declare function _BinaryenRemoveFunction( module: ModuleRef, name: StringRef, ): void; export declare function _BinaryenGetNumFunctions(module: ModuleRef): Index; export declare function _BinaryenGetFunctionByIndex( module: ModuleRef, index: Index, ): FunctionRef; export declare function _BinaryenFunctionGetName(func: FunctionRef): StringRef; export declare function _BinaryenFunctionGetParams(func: FunctionRef): TypeRef; export declare function _BinaryenFunctionGetResults(func: FunctionRef): TypeRef; export declare function _BinaryenFunctionGetNumVars(func: FunctionRef): Index; export declare function _BinaryenFunctionGetVar( func: FunctionRef, index: Index, ): TypeRef; export declare function _BinaryenFunctionGetNumLocals(func: FunctionRef): Index; export declare function _BinaryenFunctionHasLocalName( func: FunctionRef, index: Index, ): bool; export declare function _BinaryenFunctionGetLocalName( func: FunctionRef, index: Index, ): StringRef; export declare function _BinaryenFunctionSetLocalName( func: FunctionRef, index: Index, name: StringRef, ): void; export declare function _BinaryenFunctionGetBody( func: FunctionRef, ): ExpressionRef; export declare function _BinaryenFunctionSetBody( func: FunctionRef, bodyExpr: ExpressionRef, ): void; export declare function _BinaryenFunctionOptimize( func: FunctionRef, module: ModuleRef, ): void; export declare function _BinaryenFunctionRunPasses( func: FunctionRef, module: ModuleRef, passes: ArrayRef, numPasses: Index, ): void; export declare function _BinaryenFunctionSetDebugLocation( func: FunctionRef, expr: ExpressionRef, fileIndex: Index, lineNumber: Index, columnNumber: Index, ): void; export declare function _BinaryenAddFunctionImport( module: ModuleRef, internalName: StringRef, externalModuleName: StringRef, externalBaseName: StringRef, params: TypeRef, results: TypeRef, ): void; export declare function _BinaryenAddTableImport( module: ModuleRef, internalName: StringRef, externalModuleName: StringRef, externalBaseName: StringRef, ): void; export declare function _BinaryenAddMemoryImport( module: ModuleRef, internalName: StringRef, externalModuleName: StringRef, externalBaseName: StringRef, shared: bool, ): void; export declare function _BinaryenAddGlobalImport( module: ModuleRef, internalName: StringRef, externalModuleName: StringRef, externalBaseName: StringRef, globalType: TypeRef, mutable: bool, ): void; export declare function _BinaryenAddTagImport( module: ModuleRef, internalName: StringRef, externalModuleName: StringRef, externalBaseName: StringRef, params: TypeRef, results: TypeRef, ): void; export declare function _BinaryenAddFunctionExport( module: ModuleRef, internalName: StringRef, externalName: StringRef, ): ExportRef; export declare function _BinaryenAddTableExport( module: ModuleRef, internalName: StringRef, externalName: StringRef, ): ExportRef; export declare function _BinaryenAddMemoryExport( module: ModuleRef, internalName: StringRef, externalName: StringRef, ): ExportRef; export declare function _BinaryenAddGlobalExport( module: ModuleRef, internalName: StringRef, externalName: StringRef, ): ExportRef; export declare function _BinaryenAddTagExport( module: ModuleRef, internalName: StringRef, externalName: StringRef, ): ExportRef; export declare function _BinaryenGetExport( module: ModuleRef, externalName: StringRef, ): ExportRef; export declare function _BinaryenRemoveExport( module: ModuleRef, externalName: StringRef, ): void; export declare function _BinaryenGetNumExports(module: ModuleRef): Index; export declare function _BinaryenGetExportByIndex( module: ModuleRef, index: Index, ): ExportRef; export declare function _BinaryenExportGetKind(ref: ExportRef): ExternalKind; export declare function _BinaryenExportGetName(ref: ExportRef): StringRef; export declare function _BinaryenExportGetValue(ref: ExportRef): StringRef; export declare function _BinaryenAddGlobal( module: ModuleRef, name: StringRef, type: TypeRef, mutable: bool, init: ExpressionRef, ): GlobalRef; export declare function _BinaryenGetGlobal( module: ModuleRef, name: StringRef, ): GlobalRef; export declare function _BinaryenRemoveGlobal( module: ModuleRef, name: StringRef, ): void; export declare function _BinaryenGetNumGlobals(module: ModuleRef): Index; export declare function _BinaryenGetGlobalByIndex( module: ModuleRef, index: Index, ): GlobalRef; export declare function _BinaryenGlobalGetName(global: GlobalRef): StringRef; export declare function _BinaryenGlobalGetType(global: GlobalRef): TypeRef; export declare function _BinaryenGlobalIsMutable(global: GlobalRef): bool; export declare function _BinaryenGlobalGetInitExpr( global: GlobalRef, ): ExpressionRef; export declare function _BinaryenAddTag( module: ModuleRef, name: StringRef, params: TypeRef, results: TypeRef, ): TagRef; export declare function _BinaryenGetTag( module: ModuleRef, name: StringRef, ): TagRef; export declare function _BinaryenRemoveTag( module: ModuleRef, name: StringRef, ): void; export declare function _BinaryenTagGetName(tag: TagRef): StringRef; export declare function _BinaryenTagGetParams(tag: TagRef): TypeRef; export declare function _BinaryenTagGetResults(tag: TagRef): TypeRef; export declare function _BinaryenAddTable( module: ModuleRef, name: StringRef, initial: Index, maximum: Index, type: TypeRef, ): TableRef; export declare function _BinaryenRemoveTable( module: ModuleRef, table: StringRef, ): void; export declare function _BinaryenGetNumTables(module: ModuleRef): Index; export declare function _BinaryenGetTable( module: ModuleRef, name: StringRef, ): TableRef; export declare function _BinaryenGetTableByIndex( module: ModuleRef, index: Index, ): TableRef; export declare function _BinaryenTableGetName(table: TableRef): StringRef; export declare function _BinaryenTableSetName( table: TableRef, name: StringRef, ): void; export declare function _BinaryenTableGetInitial(table: TableRef): Index; export declare function _BinaryenTableSetInitial( table: TableRef, initial: Index, ): void; export declare function _BinaryenTableHasMax(table: TableRef): bool; export declare function _BinaryenTableGetMax(table: TableRef): Index; export declare function _BinaryenTableSetMax(table: TableRef, max: Index): void; export declare function _BinaryenAddActiveElementSegment( module: ModuleRef, table: StringRef, name: StringRef, funcNames: ArrayRef, numFuncNames: Index, offset: ExpressionRef, ): ElementSegmentRef; export declare function _BinaryenAddPassiveElementSegment( module: ModuleRef, name: StringRef, funcNames: ArrayRef, numFuncNames: Index, ): ElementSegmentRef; export declare function _BinaryenRemoveElementSegment( module: ModuleRef, name: StringRef, ): void; export declare function _BinaryenGetNumElementSegments( module: ModuleRef, name: StringRef, ): Index; export declare function _BinaryenGetElementSegment( module: ModuleRef, name: StringRef, ): ElementSegmentRef; export declare function _BinaryenGetElementSegmentByIndex( module: ModuleRef, index: Index, ): ElementSegmentRef; export declare function _BinaryenSetMemory( module: ModuleRef, initial: Index, maximum: Index, exportName: StringRef, segments: ArrayRef>, segmentPassive: ArrayRef, segmentOffsets: ArrayRef, segmentSizes: ArrayRef, numSegments: Index, shared: bool, memory64: bool, name: StringRef, ): void; export declare function _BinaryenGetNumMemorySegments(module: ModuleRef): Index; export declare function _BinaryenGetMemorySegmentByteOffset( module: ModuleRef, index: Index, ): u32; export declare function _BinaryenGetMemorySegmentByteLength( module: ModuleRef, id: Index, ): usize; export declare function _BinaryenCopyMemorySegmentData( module: ModuleRef, id: Index, buffer: ArrayRef, ): void; export declare function _BinaryenSetStart( module: ModuleRef, start: FunctionRef, ): void; export declare function _BinaryenModuleParse(text: StringRef): ModuleRef; export declare function _BinaryenModulePrint(module: ModuleRef): void; export declare function _BinaryenModulePrintAsmjs(module: ModuleRef): void; export declare function _BinaryenModuleValidate(module: ModuleRef): i32; export declare function _BinaryenModuleOptimize(module: ModuleRef): void; export declare function _BinaryenModuleRunPasses( module: ModuleRef, passes: ArrayRef, numPasses: Index, ): void; export declare function _BinaryenModuleAutoDrop(module: ModuleRef): void; export declare function _BinaryenSizeofAllocateAndWriteResult(): i32; export declare function _BinaryenModuleAllocateAndWrite( resultOut: BinaryenModuleAllocateAndWriteResultRef, module: ModuleRef, sourceMapUrl: StringRef, ): void; export declare function _BinaryenModuleAllocateAndWriteText( module: ModuleRef, ): StringRef; export declare function _BinaryenModuleAllocateAndWriteStackIR( module: ModuleRef, optimize: bool, ): StringRef; export declare function _BinaryenModuleRead( input: ArrayRef, inputSize: usize, ): ModuleRef; export declare function _BinaryenModuleInterpret(module: ModuleRef): void; export declare function _BinaryenModuleAddDebugInfoFileName( module: ModuleRef, filename: StringRef, ): Index; export declare function _BinaryenModuleGetDebugInfoFileName( module: ModuleRef, index: Index, ): StringRef; export declare function _BinaryenModuleGetFeatures( module: ModuleRef, ): FeatureFlags; export declare function _BinaryenModuleSetFeatures( module: ModuleRef, featureFlags: FeatureFlags, ): void; export declare function _BinaryenAddCustomSection( module: ModuleRef, name: StringRef, contents: ArrayRef, contentsSize: Index, ): void; export declare function _BinaryenExpressionGetSideEffects( expr: ExpressionRef, module: ModuleRef, ): SideEffects; export declare function _RelooperCreate(module: ModuleRef): RelooperRef; export declare function _RelooperAddBlock( relooper: RelooperRef, code: ExpressionRef, ): RelooperBlockRef; export declare function _RelooperAddBranch( from: RelooperBlockRef, to: RelooperBlockRef, condition: ExpressionRef, code: ExpressionRef, ): void; export declare function _RelooperAddBlockWithSwitch( relooper: RelooperRef, code: ExpressionRef, condition: ExpressionRef, ): RelooperBlockRef; export declare function _RelooperAddBranchForSwitch( from: RelooperBlockRef, to: RelooperBlockRef, indexes: ArrayRef, numIndexes: Index, code: ExpressionRef, ): void; export declare function _RelooperRenderAndDispose( relooper: RelooperRef, entry: RelooperBlockRef, labelHelper: Index, ): ExpressionRef; export declare function _ExpressionRunnerCreate( module: ModuleRef, flags: ExpressionRunnerFlags, maxDepth: Index, maxLoopIterations: Index, ): ExpressionRunnerRef; export declare function _ExpressionRunnerSetLocalValue( runner: ExpressionRunnerRef, index: Index, value: ExpressionRef, ): bool; export declare function _ExpressionRunnerSetGlobalValue( runner: ExpressionRunnerRef, name: StringRef, value: ExpressionRef, ): bool; export declare function _ExpressionRunnerRunAndDispose( runner: ExpressionRunnerRef, expr: ExpressionRef, ): ExpressionRef; export declare function _TypeBuilderCreate(size: Index): TypeBuilderRef; export declare function _TypeBuilderGrow( builder: TypeBuilderRef, count: Index, ): void; export declare function _TypeBuilderGetSize(builder: TypeBuilderRef): Index; export declare function _TypeBuilderSetBasicHeapType( builder: TypeBuilderRef, index: Index, basicHeapType: HeapTypeRef, ): void; export declare function _TypeBuilderSetSignatureType( builder: TypeBuilderRef, index: Index, paramTypes: TypeRef, resultTypes: TypeRef, ): void; export declare function _TypeBuilderSetStructType( builder: TypeBuilderRef, index: Index, fieldTypes: ArrayRef, fieldPackedTypes: ArrayRef, fieldMutables: ArrayRef, numFields: i32, ): void; export declare function _TypeBuilderSetArrayType( builder: TypeBuilderRef, index: Index, elementType: TypeRef, elementPackedTyype: PackedType, elementMutable: bool, ): void; export declare function _TypeBuilderIsBasic( builder: TypeBuilderRef, index: Index, ): bool; export declare function _TypeBuilderGetBasic( builder: TypeBuilderRef, index: Index, ): HeapTypeRef; export declare function _TypeBuilderGetTempHeapType( builder: TypeBuilderRef, index: Index, ): HeapTypeRef; export declare function _TypeBuilderGetTempTupleType( builder: TypeBuilderRef, types: ArrayRef, numTypes: Index, ): TypeRef; export declare function _TypeBuilderGetTempRefType( builder: TypeBuilderRef, heapType: HeapTypeRef, nullable: bool, ): TypeRef; export declare function _TypeBuilderSetSubType( builder: TypeBuilderRef, index: Index, superType: HeapTypeRef, ): void; export declare function _TypeBuilderSetOpen( builder: TypeBuilderRef, index: Index, ): void; export declare function _TypeBuilderCreateRecGroup( builder: TypeBuilderRef, index: Index, length: Index, ): void; export declare function _TypeBuilderBuildAndDispose( builder: TypeBuilderRef, heapTypes: ArrayRef, errorIndex: Pointer, errorReason: Pointer, ): bool; export declare function _BinaryenModuleSetTypeName( module: ModuleRef, heapType: HeapTypeRef, name: StringRef, ): void; export declare function _BinaryenModuleSetFieldName( module: ModuleRef, heapType: HeapTypeRef, index: Index, name: StringRef, ): void; export declare function _BinaryenGetOptimizeLevel(): i32; export declare function _BinaryenSetOptimizeLevel(level: i32): void; export declare function _BinaryenGetShrinkLevel(): i32; export declare function _BinaryenSetShrinkLevel(level: i32): void; export declare function _BinaryenGetDebugInfo(): bool; export declare function _BinaryenSetDebugInfo(on: bool): void; export declare function _BinaryenGetLowMemoryUnused(): bool; export declare function _BinaryenSetLowMemoryUnused(on: bool): void; export declare function _BinaryenGetZeroFilledMemory(): bool; export declare function _BinaryenSetZeroFilledMemory(on: bool): void; export declare function _BinaryenGetFastMath(): bool; export declare function _BinaryenSetFastMath(on: bool): void; export declare function _BinaryenGetPassArgument(key: StringRef): StringRef; export declare function _BinaryenSetPassArgument( key: StringRef, value: StringRef, ): void; export declare function _BinaryenClearPassArguments(): void; export declare function _BinaryenGetAlwaysInlineMaxSize(): Index; export declare function _BinaryenSetAlwaysInlineMaxSize(size: Index): void; export declare function _BinaryenGetFlexibleInlineMaxSize(): Index; export declare function _BinaryenSetFlexibleInlineMaxSize(size: Index): void; export declare function _BinaryenGetOneCallerInlineMaxSize(): Index; export declare function _BinaryenSetOneCallerInlineMaxSize(size: Index): void; export declare function _BinaryenGetAllowInliningFunctionsWithLoops(): bool; export declare function _BinaryenSetAllowInliningFunctionsWithLoops( enabled: bool, ): void; export declare function _BinaryenGetTypeSystem(): TypeSystem; export declare function _BinaryenSetTypeSystem(typeSystem: TypeSystem): void; // Helpers export declare function _malloc(size: usize): usize; export declare function _free(ptr: usize): void; export declare function __i32_store8(ptr: usize, value: number): void; export declare function __i32_store16(ptr: usize, value: number): void; export declare function __i32_store(ptr: usize, value: number): void; export declare function __f32_store(ptr: usize, value: number): void; export declare function __f64_store(ptr: usize, value: number): void; export declare function __i32_load8_s(ptr: usize): i8; export declare function __i32_load8_u(ptr: usize): u8; export declare function __i32_load16_s(ptr: usize): i16; export declare function __i32_load16_u(ptr: usize): u16; export declare function __i32_load(ptr: usize): i32; export declare function __f32_load(ptr: usize): f32; export declare function __f64_load(ptr: usize): f64; ================================================ FILE: src/backend/binaryen/glue/binaryen.js ================================================ /** * @fileoverview Binaryen glue code for JavaScript. * @license Apache-2.0 */ /* This file is modified base on: * https://github.com/AssemblyScript/assemblyscript/blob/main/src/glue/binaryen.js * Please see original LICENSE and NOTICE under the same folder */ import binaryen from "binaryen"; export const { _BinaryenTypeCreate, _BinaryenTypeArity, _BinaryenTypeExpand, _BinaryenTypeGetHeapType, _BinaryenTypeFromHeapType, _BinaryenTypeIsNullable, _BinaryenTypeFuncref, _BinaryenTypeExternref, _BinaryenTypeAnyref, _BinaryenTypeEqref, _BinaryenTypeI31ref, _BinaryenTypeStructref, _BinaryenTypeArrayref, _BinaryenTypeStringref, _BinaryenTypeStringviewWTF8, _BinaryenTypeStringviewWTF16, _BinaryenTypeStringviewIter, _BinaryenTypeNullref, _BinaryenTypeNullExternref, _BinaryenTypeNullFuncref, _BinaryenHeapTypeFunc, _BinaryenHeapTypeExt, _BinaryenHeapTypeAny, _BinaryenHeapTypeEq, _BinaryenHeapTypeI31, _BinaryenHeapTypeStruct, _BinaryenHeapTypeArray, _BinaryenHeapTypeString, _BinaryenHeapTypeStringviewWTF8, _BinaryenHeapTypeStringviewWTF16, _BinaryenHeapTypeStringviewIter, _BinaryenHeapTypeNone, _BinaryenHeapTypeNoext, _BinaryenHeapTypeNofunc, _BinaryenHeapTypeIsBasic, _BinaryenHeapTypeIsSignature, _BinaryenHeapTypeIsStruct, _BinaryenHeapTypeIsArray, _BinaryenHeapTypeIsBottom, _BinaryenHeapTypeGetBottom, _BinaryenHeapTypeIsSubType, _BinaryenStructTypeGetNumFields, _BinaryenStructTypeGetFieldType, _BinaryenStructTypeGetFieldPackedType, _BinaryenStructTypeIsFieldMutable, _BinaryenArrayTypeGetElementType, _BinaryenArrayTypeGetElementPackedType, _BinaryenArrayTypeIsElementMutable, _BinaryenSignatureTypeGetParams, _BinaryenSignatureTypeGetResults, _BinaryenModuleCreate, _BinaryenModuleDispose, _BinaryenSizeofLiteral, _BinaryenLiteralInt32, _BinaryenLiteralInt64, _BinaryenLiteralFloat32, _BinaryenLiteralFloat64, _BinaryenLiteralVec128, _BinaryenLiteralFloat32Bits, _BinaryenLiteralFloat64Bits, _BinaryenExpressionGetId, _BinaryenExpressionGetType, _BinaryenExpressionSetType, _BinaryenExpressionPrint, _BinaryenExpressionCopy, _BinaryenExpressionFinalize, _BinaryenBlock, _BinaryenBlockGetName, _BinaryenBlockSetName, _BinaryenBlockGetNumChildren, _BinaryenBlockGetChildAt, _BinaryenBlockSetChildAt, _BinaryenBlockAppendChild, _BinaryenBlockInsertChildAt, _BinaryenBlockRemoveChildAt, _BinaryenIf, _BinaryenIfGetCondition, _BinaryenIfSetCondition, _BinaryenIfGetIfTrue, _BinaryenIfSetIfTrue, _BinaryenIfGetIfFalse, _BinaryenIfSetIfFalse, _BinaryenLoop, _BinaryenLoopGetName, _BinaryenLoopSetName, _BinaryenLoopGetBody, _BinaryenLoopSetBody, _BinaryenBreak, _BinaryenBreakGetName, _BinaryenBreakSetName, _BinaryenBreakGetCondition, _BinaryenBreakSetCondition, _BinaryenBreakGetValue, _BinaryenBreakSetValue, _BinaryenSwitch, _BinaryenSwitchGetNumNames, _BinaryenSwitchGetNameAt, _BinaryenSwitchSetNameAt, _BinaryenSwitchAppendName, _BinaryenSwitchInsertNameAt, _BinaryenSwitchRemoveNameAt, _BinaryenSwitchGetDefaultName, _BinaryenSwitchSetDefaultName, _BinaryenSwitchGetCondition, _BinaryenSwitchSetCondition, _BinaryenSwitchGetValue, _BinaryenSwitchSetValue, _BinaryenCall, _BinaryenCallGetTarget, _BinaryenCallSetTarget, _BinaryenCallGetNumOperands, _BinaryenCallGetOperandAt, _BinaryenCallSetOperandAt, _BinaryenCallAppendOperand, _BinaryenCallInsertOperandAt, _BinaryenCallRemoveOperandAt, _BinaryenCallIsReturn, _BinaryenCallSetReturn, _BinaryenReturnCall, _BinaryenCallIndirect, _BinaryenCallIndirectGetTable, _BinaryenCallIndirectSetTable, _BinaryenCallIndirectGetTarget, _BinaryenCallIndirectSetTarget, _BinaryenCallIndirectGetNumOperands, _BinaryenCallIndirectGetOperandAt, _BinaryenCallIndirectSetOperandAt, _BinaryenCallIndirectAppendOperand, _BinaryenCallIndirectInsertOperandAt, _BinaryenCallIndirectRemoveOperandAt, _BinaryenCallIndirectIsReturn, _BinaryenCallIndirectSetReturn, _BinaryenReturnCallIndirect, _BinaryenLocalGet, _BinaryenLocalGetGetIndex, _BinaryenLocalGetSetIndex, _BinaryenLocalSet, _BinaryenLocalSetIsTee, _BinaryenLocalSetGetIndex, _BinaryenLocalSetSetIndex, _BinaryenLocalSetGetValue, _BinaryenLocalSetSetValue, _BinaryenLocalTee, _BinaryenGlobalGet, _BinaryenGlobalGetGetName, _BinaryenGlobalGetSetName, _BinaryenGlobalSet, _BinaryenGlobalSetGetName, _BinaryenGlobalSetSetName, _BinaryenGlobalSetGetValue, _BinaryenGlobalSetSetValue, _BinaryenMemorySize, _BinaryenMemoryGrow, _BinaryenMemoryGrowGetDelta, _BinaryenMemoryGrowSetDelta, _BinaryenLoad, _BinaryenLoadIsAtomic, _BinaryenLoadSetAtomic, _BinaryenLoadIsSigned, _BinaryenLoadSetSigned, _BinaryenLoadGetOffset, _BinaryenLoadSetOffset, _BinaryenLoadGetBytes, _BinaryenLoadSetBytes, _BinaryenLoadGetAlign, _BinaryenLoadSetAlign, _BinaryenLoadGetPtr, _BinaryenLoadSetPtr, _BinaryenAtomicLoad, _BinaryenStore, _BinaryenStoreIsAtomic, _BinaryenStoreSetAtomic, _BinaryenStoreGetBytes, _BinaryenStoreSetBytes, _BinaryenStoreGetOffset, _BinaryenStoreSetOffset, _BinaryenStoreGetAlign, _BinaryenStoreSetAlign, _BinaryenStoreGetPtr, _BinaryenStoreSetPtr, _BinaryenStoreGetValue, _BinaryenStoreSetValue, _BinaryenStoreGetValueType, _BinaryenStoreSetValueType, _BinaryenAtomicStore, _BinaryenConst, _BinaryenConstGetValueI32, _BinaryenConstSetValueI32, _BinaryenConstGetValueI64Low, _BinaryenConstSetValueI64Low, _BinaryenConstGetValueI64High, _BinaryenConstSetValueI64High, _BinaryenConstGetValueF32, _BinaryenConstSetValueF32, _BinaryenConstGetValueF64, _BinaryenConstSetValueF64, _BinaryenConstGetValueV128, _BinaryenConstSetValueV128, _BinaryenUnary, _BinaryenUnaryGetOp, _BinaryenUnarySetOp, _BinaryenUnaryGetValue, _BinaryenUnarySetValue, _BinaryenBinary, _BinaryenBinaryGetOp, _BinaryenBinarySetOp, _BinaryenBinaryGetLeft, _BinaryenBinarySetLeft, _BinaryenBinaryGetRight, _BinaryenBinarySetRight, _BinaryenSelect, _BinaryenSelectGetIfTrue, _BinaryenSelectSetIfTrue, _BinaryenSelectGetIfFalse, _BinaryenSelectSetIfFalse, _BinaryenSelectGetCondition, _BinaryenSelectSetCondition, _BinaryenDrop, _BinaryenDropGetValue, _BinaryenDropSetValue, _BinaryenReturn, _BinaryenReturnGetValue, _BinaryenReturnSetValue, _BinaryenNop, _BinaryenUnreachable, _BinaryenAtomicRMW, _BinaryenAtomicRMWGetOp, _BinaryenAtomicRMWSetOp, _BinaryenAtomicRMWGetBytes, _BinaryenAtomicRMWSetBytes, _BinaryenAtomicRMWGetOffset, _BinaryenAtomicRMWSetOffset, _BinaryenAtomicRMWGetPtr, _BinaryenAtomicRMWSetPtr, _BinaryenAtomicRMWGetValue, _BinaryenAtomicRMWSetValue, _BinaryenAtomicCmpxchg, _BinaryenAtomicCmpxchgGetBytes, _BinaryenAtomicCmpxchgSetBytes, _BinaryenAtomicCmpxchgGetOffset, _BinaryenAtomicCmpxchgSetOffset, _BinaryenAtomicCmpxchgGetPtr, _BinaryenAtomicCmpxchgSetPtr, _BinaryenAtomicCmpxchgGetExpected, _BinaryenAtomicCmpxchgSetExpected, _BinaryenAtomicCmpxchgGetReplacement, _BinaryenAtomicCmpxchgSetReplacement, _BinaryenAtomicWait, _BinaryenAtomicWaitGetPtr, _BinaryenAtomicWaitSetPtr, _BinaryenAtomicWaitGetExpected, _BinaryenAtomicWaitSetExpected, _BinaryenAtomicWaitGetTimeout, _BinaryenAtomicWaitSetTimeout, _BinaryenAtomicWaitGetExpectedType, _BinaryenAtomicWaitSetExpectedType, _BinaryenAtomicNotify, _BinaryenAtomicNotifyGetPtr, _BinaryenAtomicNotifySetPtr, _BinaryenAtomicNotifyGetNotifyCount, _BinaryenAtomicNotifySetNotifyCount, _BinaryenAtomicFence, _BinaryenAtomicFenceGetOrder, _BinaryenAtomicFenceSetOrder, _BinaryenSIMDExtract, _BinaryenSIMDExtractGetOp, _BinaryenSIMDExtractSetOp, _BinaryenSIMDExtractGetVec, _BinaryenSIMDExtractSetVec, _BinaryenSIMDExtractGetIndex, _BinaryenSIMDExtractSetIndex, _BinaryenSIMDReplace, _BinaryenSIMDReplaceGetOp, _BinaryenSIMDReplaceSetOp, _BinaryenSIMDReplaceGetVec, _BinaryenSIMDReplaceSetVec, _BinaryenSIMDReplaceGetIndex, _BinaryenSIMDReplaceSetIndex, _BinaryenSIMDReplaceGetValue, _BinaryenSIMDReplaceSetValue, _BinaryenSIMDShuffle, _BinaryenSIMDShuffleGetLeft, _BinaryenSIMDShuffleSetLeft, _BinaryenSIMDShuffleGetRight, _BinaryenSIMDShuffleSetRight, _BinaryenSIMDShuffleGetMask, _BinaryenSIMDShuffleSetMask, _BinaryenSIMDTernary, _BinaryenSIMDTernaryGetOp, _BinaryenSIMDTernarySetOp, _BinaryenSIMDTernaryGetA, _BinaryenSIMDTernarySetA, _BinaryenSIMDTernaryGetB, _BinaryenSIMDTernarySetB, _BinaryenSIMDTernaryGetC, _BinaryenSIMDTernarySetC, _BinaryenSIMDShift, _BinaryenSIMDShiftGetOp, _BinaryenSIMDShiftSetOp, _BinaryenSIMDShiftGetVec, _BinaryenSIMDShiftSetVec, _BinaryenSIMDShiftGetShift, _BinaryenSIMDShiftSetShift, _BinaryenSIMDLoad, _BinaryenSIMDLoadGetOp, _BinaryenSIMDLoadSetOp, _BinaryenSIMDLoadGetOffset, _BinaryenSIMDLoadSetOffset, _BinaryenSIMDLoadGetAlign, _BinaryenSIMDLoadSetAlign, _BinaryenSIMDLoadGetPtr, _BinaryenSIMDLoadSetPtr, _BinaryenSIMDLoadStoreLane, _BinaryenSIMDLoadStoreLaneGetOp, _BinaryenSIMDLoadStoreLaneSetOp, _BinaryenSIMDLoadStoreLaneGetOffset, _BinaryenSIMDLoadStoreLaneSetOffset, _BinaryenSIMDLoadStoreLaneGetAlign, _BinaryenSIMDLoadStoreLaneSetAlign, _BinaryenSIMDLoadStoreLaneGetIndex, _BinaryenSIMDLoadStoreLaneSetIndex, _BinaryenSIMDLoadStoreLaneGetPtr, _BinaryenSIMDLoadStoreLaneSetPtr, _BinaryenSIMDLoadStoreLaneGetVec, _BinaryenSIMDLoadStoreLaneSetVec, _BinaryenSIMDLoadStoreLaneIsStore, _BinaryenMemoryInit, _BinaryenMemoryInitGetSegment, _BinaryenMemoryInitSetSegment, _BinaryenMemoryInitGetDest, _BinaryenMemoryInitSetDest, _BinaryenMemoryInitGetOffset, _BinaryenMemoryInitSetOffset, _BinaryenMemoryInitGetSize, _BinaryenMemoryInitSetSize, _BinaryenDataDrop, _BinaryenDataDropGetSegment, _BinaryenDataDropSetSegment, _BinaryenMemoryCopy, _BinaryenMemoryCopyGetDest, _BinaryenMemoryCopySetDest, _BinaryenMemoryCopyGetSource, _BinaryenMemoryCopySetSource, _BinaryenMemoryCopyGetSize, _BinaryenMemoryCopySetSize, _BinaryenMemoryFill, _BinaryenMemoryFillGetDest, _BinaryenMemoryFillSetDest, _BinaryenMemoryFillGetValue, _BinaryenMemoryFillSetValue, _BinaryenMemoryFillGetSize, _BinaryenMemoryFillSetSize, _BinaryenRefNull, _BinaryenRefIsNull, _BinaryenRefIsNullGetValue, _BinaryenRefIsNullSetValue, _BinaryenRefAs, _BinaryenRefAsGetOp, _BinaryenRefAsSetOp, _BinaryenRefAsGetValue, _BinaryenRefAsSetValue, _BinaryenRefFunc, _BinaryenRefFuncGetFunc, _BinaryenRefFuncSetFunc, _BinaryenRefEq, _BinaryenRefEqGetLeft, _BinaryenRefEqSetLeft, _BinaryenRefEqGetRight, _BinaryenRefEqSetRight, _BinaryenTableGet, _BinaryenTableGetGetTable, _BinaryenTableGetSetTable, _BinaryenTableGetGetIndex, _BinaryenTableGetSetIndex, _BinaryenTableSet, _BinaryenTableSetGetTable, _BinaryenTableSetSetTable, _BinaryenTableSetGetIndex, _BinaryenTableSetSetIndex, _BinaryenTableSetGetValue, _BinaryenTableSetSetValue, _BinaryenTableSize, _BinaryenTableSizeGetTable, _BinaryenTableSizeSetTable, _BinaryenTableGrow, _BinaryenTableGrowGetTable, _BinaryenTableGrowSetTable, _BinaryenTableGrowGetValue, _BinaryenTableGrowSetValue, _BinaryenTableGrowGetDelta, _BinaryenTableGrowSetDelta, _BinaryenTry, _BinaryenTryGetName, _BinaryenTrySetName, _BinaryenTryGetBody, _BinaryenTrySetBody, _BinaryenTryGetNumCatchTags, _BinaryenTryGetNumCatchBodies, _BinaryenTryGetCatchTagAt, _BinaryenTrySetCatchTagAt, _BinaryenTryAppendCatchTag, _BinaryenTryInsertCatchTagAt, _BinaryenTryRemoveCatchTagAt, _BinaryenTryGetCatchBodyAt, _BinaryenTrySetCatchBodyAt, _BinaryenTryAppendCatchBody, _BinaryenTryInsertCatchBodyAt, _BinaryenTryRemoveCatchBodyAt, _BinaryenTryHasCatchAll, _BinaryenTryGetDelegateTarget, _BinaryenTrySetDelegateTarget, _BinaryenTryIsDelegate, _BinaryenThrow, _BinaryenThrowGetTag, _BinaryenThrowSetTag, _BinaryenThrowGetNumOperands, _BinaryenThrowGetOperandAt, _BinaryenThrowSetOperandAt, _BinaryenThrowAppendOperand, _BinaryenThrowInsertOperandAt, _BinaryenThrowRemoveOperandAt, _BinaryenRethrow, _BinaryenRethrowGetTarget, _BinaryenRethrowSetDepth, _BinaryenTupleMake, _BinaryenTupleMakeGetNumOperands, _BinaryenTupleMakeGetOperandAt, _BinaryenTupleMakeSetOperandAt, _BinaryenTupleMakeAppendOperand, _BinaryenTupleMakeInsertOperandAt, _BinaryenTupleMakeRemoveOperandAt, _BinaryenTupleExtract, _BinaryenTupleExtractGetTuple, _BinaryenTupleExtractSetTuple, _BinaryenTupleExtractGetIndex, _BinaryenTupleExtractSetIndex, _BinaryenPop, _BinaryenI31New, _BinaryenI31NewGetValue, _BinaryenI31NewSetValue, _BinaryenI31Get, _BinaryenI31GetGetI31, _BinaryenI31GetSetI31, _BinaryenI31GetIsSigned, _BinaryenI31GetSetSigned, _BinaryenCallRef, _BinaryenCallRefGetNumOperands, _BinaryenCallRefGetOperandAt, _BinaryenCallRefSetOperandAt, _BinaryenCallRefAppendOperand, _BinaryenCallRefInsertOperandAt, _BinaryenCallRefRemoveOperandAt, _BinaryenCallRefGetTarget, _BinaryenCallRefSetTarget, _BinaryenCallRefIsReturn, _BinaryenCallRefSetReturn, _BinaryenRefTest, _BinaryenRefTestGetRef, _BinaryenRefTestSetRef, _BinaryenRefTestGetCastType, _BinaryenRefTestSetCastType, _BinaryenRefCast, _BinaryenRefCastGetRef, _BinaryenRefCastSetRef, _BinaryenBrOn, _BinaryenBrOnGetOp, _BinaryenBrOnSetOp, _BinaryenBrOnGetName, _BinaryenBrOnSetName, _BinaryenBrOnGetRef, _BinaryenBrOnSetRef, _BinaryenBrOnGetCastType, _BinaryenBrOnSetCastType, _BinaryenStructNew, _BinaryenStructNewGetNumOperands, _BinaryenStructNewGetOperandAt, _BinaryenStructNewSetOperandAt, _BinaryenStructNewAppendOperand, _BinaryenStructNewInsertOperandAt, _BinaryenStructNewRemoveOperandAt, _BinaryenStructGet, _BinaryenStructGetGetIndex, _BinaryenStructGetSetIndex, _BinaryenStructGetGetRef, _BinaryenStructGetSetRef, _BinaryenStructGetIsSigned, _BinaryenStructGetSetSigned, _BinaryenStructSet, _BinaryenStructSetGetIndex, _BinaryenStructSetSetIndex, _BinaryenStructSetGetRef, _BinaryenStructSetSetRef, _BinaryenStructSetGetValue, _BinaryenStructSetSetValue, _BinaryenArrayNew, _BinaryenArrayNewGetInit, _BinaryenArrayNewSetInit, _BinaryenArrayNewGetSize, _BinaryenArrayNewSetSize, _BinaryenArrayNewFixed, _BinaryenArrayNewFixedGetNumValues, _BinaryenArrayNewFixedGetValueAt, _BinaryenArrayNewFixedSetValueAt, _BinaryenArrayNewFixedAppendValue, _BinaryenArrayNewFixedInsertValueAt, _BinaryenArrayNewFixedRemoveValueAt, _BinaryenArrayGet, _BinaryenArrayGetGetRef, _BinaryenArrayGetSetRef, _BinaryenArrayGetGetIndex, _BinaryenArrayGetSetIndex, _BinaryenArrayGetIsSigned, _BinaryenArrayGetSetSigned, _BinaryenArraySet, _BinaryenArraySetGetRef, _BinaryenArraySetSetRef, _BinaryenArraySetGetIndex, _BinaryenArraySetSetIndex, _BinaryenArraySetGetValue, _BinaryenArraySetSetValue, _BinaryenArrayLen, _BinaryenArrayLenGetRef, _BinaryenArrayLenSetRef, _BinaryenArrayCopy, _BinaryenArrayCopyGetDestRef, _BinaryenArrayCopySetDestRef, _BinaryenArrayCopyGetDestIndex, _BinaryenArrayCopySetDestIndex, _BinaryenArrayCopyGetSrcRef, _BinaryenArrayCopySetSrcRef, _BinaryenArrayCopyGetSrcIndex, _BinaryenArrayCopySetSrcIndex, _BinaryenArrayCopyGetLength, _BinaryenArrayCopySetLength, _BinaryenStringNew, _BinaryenStringNewGetOp, _BinaryenStringNewSetOp, _BinaryenStringNewGetPtr, _BinaryenStringNewSetPtr, _BinaryenStringNewGetLength, _BinaryenStringNewSetLength, _BinaryenStringNewGetStart, _BinaryenStringNewSetStart, _BinaryenStringNewGetEnd, _BinaryenStringNewSetEnd, _BinaryenStringNewIsTry, _BinaryenStringNewSetTry, _BinaryenStringConst, _BinaryenStringConstGetString, _BinaryenStringConstSetString, _BinaryenStringMeasure, _BinaryenStringMeasureGetOp, _BinaryenStringMeasureSetOp, _BinaryenStringMeasureGetRef, _BinaryenStringMeasureSetRef, _BinaryenStringEncode, _BinaryenStringEncodeGetOp, _BinaryenStringEncodeSetOp, _BinaryenStringEncodeGetRef, _BinaryenStringEncodeSetRef, _BinaryenStringEncodeGetPtr, _BinaryenStringEncodeSetPtr, _BinaryenStringEncodeGetStart, _BinaryenStringEncodeSetStart, _BinaryenStringConcat, _BinaryenStringConcatGetLeft, _BinaryenStringConcatSetLeft, _BinaryenStringConcatGetRight, _BinaryenStringConcatSetRight, _BinaryenStringEq, _BinaryenStringEqGetOp, _BinaryenStringEqSetOp, _BinaryenStringEqGetLeft, _BinaryenStringEqSetLeft, _BinaryenStringEqGetRight, _BinaryenStringEqSetRight, _BinaryenStringAs, _BinaryenStringAsGetOp, _BinaryenStringAsSetOp, _BinaryenStringAsGetRef, _BinaryenStringAsSetRef, _BinaryenStringWTF8Advance, _BinaryenStringWTF8AdvanceGetRef, _BinaryenStringWTF8AdvanceSetRef, _BinaryenStringWTF8AdvanceGetPos, _BinaryenStringWTF8AdvanceSetPos, _BinaryenStringWTF8AdvanceGetBytes, _BinaryenStringWTF8AdvanceSetBytes, _BinaryenStringWTF16Get, _BinaryenStringWTF16GetGetRef, _BinaryenStringWTF16GetSetRef, _BinaryenStringWTF16GetGetPos, _BinaryenStringWTF16GetSetPos, _BinaryenStringIterNext, _BinaryenStringIterNextGetRef, _BinaryenStringIterNextSetRef, _BinaryenStringIterMove, _BinaryenStringIterMoveGetOp, _BinaryenStringIterMoveSetOp, _BinaryenStringIterMoveGetRef, _BinaryenStringIterMoveSetRef, _BinaryenStringIterMoveGetNum, _BinaryenStringIterMoveSetNum, _BinaryenStringSliceWTF, _BinaryenStringSliceWTFGetOp, _BinaryenStringSliceWTFSetOp, _BinaryenStringSliceWTFGetRef, _BinaryenStringSliceWTFSetRef, _BinaryenStringSliceWTFGetStart, _BinaryenStringSliceWTFSetStart, _BinaryenStringSliceWTFGetEnd, _BinaryenStringSliceWTFSetEnd, _BinaryenStringSliceIter, _BinaryenStringSliceIterGetRef, _BinaryenStringSliceIterSetRef, _BinaryenStringSliceIterGetNum, _BinaryenStringSliceIterSetNum, _BinaryenAddFunction, _BinaryenAddFunctionWithHeapType, _BinaryenGetFunction, _BinaryenRemoveFunction, _BinaryenGetNumFunctions, _BinaryenGetFunctionByIndex, _BinaryenFunctionGetName, _BinaryenFunctionGetParams, _BinaryenFunctionGetResults, _BinaryenFunctionGetNumVars, _BinaryenFunctionGetVar, _BinaryenFunctionGetNumLocals, _BinaryenFunctionHasLocalName, _BinaryenFunctionGetLocalName, _BinaryenFunctionSetLocalName, _BinaryenFunctionGetBody, _BinaryenFunctionSetBody, _BinaryenFunctionOptimize, _BinaryenFunctionRunPasses, _BinaryenFunctionSetDebugLocation, _BinaryenAddFunctionImport, _BinaryenAddTableImport, _BinaryenAddMemoryImport, _BinaryenAddGlobalImport, _BinaryenAddTagImport, _BinaryenAddFunctionExport, _BinaryenAddTableExport, _BinaryenAddMemoryExport, _BinaryenAddGlobalExport, _BinaryenAddTagExport, _BinaryenGetExport, _BinaryenRemoveExport, _BinaryenGetNumExports, _BinaryenGetExportByIndex, _BinaryenExportGetKind, _BinaryenExportGetName, _BinaryenExportGetValue, _BinaryenAddGlobal, _BinaryenGetGlobal, _BinaryenRemoveGlobal, _BinaryenGetNumGlobals, _BinaryenGetGlobalByIndex, _BinaryenGlobalGetName, _BinaryenGlobalGetType, _BinaryenGlobalIsMutable, _BinaryenGlobalGetInitExpr, _BinaryenAddTag, _BinaryenGetTag, _BinaryenRemoveTag, _BinaryenTagGetName, _BinaryenTagGetParams, _BinaryenTagGetResults, _BinaryenAddTable, _BinaryenRemoveTable, _BinaryenGetNumTables, _BinaryenGetTable, _BinaryenGetTableByIndex, _BinaryenTableGetName, _BinaryenTableSetName, _BinaryenTableGetInitial, _BinaryenTableSetInitial, _BinaryenTableHasMax, _BinaryenTableGetMax, _BinaryenTableSetMax, _BinaryenAddActiveElementSegment, _BinaryenAddPassiveElementSegment, _BinaryenRemoveElementSegment, _BinaryenGetNumElementSegments, _BinaryenGetElementSegment, _BinaryenGetElementSegmentByIndex, _BinaryenSetMemory, _BinaryenGetNumMemorySegments, _BinaryenGetMemorySegmentByteOffset, _BinaryenGetMemorySegmentByteLength, _BinaryenCopyMemorySegmentData, _BinaryenSetStart, _BinaryenModuleParse, _BinaryenModulePrint, _BinaryenModulePrintAsmjs, _BinaryenModuleValidate, _BinaryenModuleOptimize, _BinaryenModuleRunPasses, _BinaryenModuleAutoDrop, _BinaryenSizeofAllocateAndWriteResult, _BinaryenModuleAllocateAndWrite, _BinaryenModuleAllocateAndWriteText, _BinaryenModuleAllocateAndWriteStackIR, _BinaryenModuleRead, _BinaryenModuleInterpret, _BinaryenModuleAddDebugInfoFileName, _BinaryenModuleGetDebugInfoFileName, _BinaryenModuleGetFeatures, _BinaryenModuleSetFeatures, _BinaryenAddCustomSection, _BinaryenExpressionGetSideEffects, _RelooperCreate, _RelooperAddBlock, _RelooperAddBranch, _RelooperAddBlockWithSwitch, _RelooperAddBranchForSwitch, _RelooperRenderAndDispose, _ExpressionRunnerCreate, _ExpressionRunnerSetLocalValue, _ExpressionRunnerSetGlobalValue, _ExpressionRunnerRunAndDispose, _TypeBuilderCreate, _TypeBuilderGrow, _TypeBuilderGetSize, _TypeBuilderSetBasicHeapType, _TypeBuilderSetSignatureType, _TypeBuilderSetStructType, _TypeBuilderSetArrayType, _TypeBuilderIsBasic, _TypeBuilderGetBasic, _TypeBuilderGetTempHeapType, _TypeBuilderGetTempTupleType, _TypeBuilderGetTempRefType, _TypeBuilderSetSubType, _TypeBuilderSetOpen, _TypeBuilderCreateRecGroup, _TypeBuilderBuildAndDispose, _BinaryenModuleSetTypeName, _BinaryenModuleSetFieldName, _BinaryenGetOptimizeLevel, _BinaryenSetOptimizeLevel, _BinaryenGetShrinkLevel, _BinaryenSetShrinkLevel, _BinaryenGetDebugInfo, _BinaryenSetDebugInfo, _BinaryenGetLowMemoryUnused, _BinaryenSetLowMemoryUnused, _BinaryenGetZeroFilledMemory, _BinaryenSetZeroFilledMemory, _BinaryenGetFastMath, _BinaryenSetFastMath, _BinaryenGetPassArgument, _BinaryenSetPassArgument, _BinaryenClearPassArguments, _BinaryenGetAlwaysInlineMaxSize, _BinaryenSetAlwaysInlineMaxSize, _BinaryenGetFlexibleInlineMaxSize, _BinaryenSetFlexibleInlineMaxSize, _BinaryenGetOneCallerInlineMaxSize, _BinaryenSetOneCallerInlineMaxSize, _BinaryenGetAllowInliningFunctionsWithLoops, _BinaryenSetAllowInliningFunctionsWithLoops, _BinaryenGetTypeSystem, _BinaryenSetTypeSystem, // Helpers _malloc, _free, __i32_store8, __i32_store16, __i32_store, __f32_store, __f64_store, __i32_load8_s, __i32_load8_u, __i32_load16_s, __i32_load16_u, __i32_load, __f32_load, __f64_load } = binaryen; export default binaryen; ================================================ FILE: src/backend/binaryen/glue/packType.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { i8ArrayType, stringType, numberArrayType, stringArrayType, boolArrayType, anyArrayType, objectStructType, infcType, stringArrayStructType, stringrefArrayType, stringrefArrayStructType, arrayBufferType, dataViewType, numberArrayStructType, i32ArrayType, } from './transform.js'; import { typeInfo } from './utils.js'; export const i8ArrayTypeInfo: typeInfo = i8ArrayType; export const stringTypeInfo: typeInfo = stringType; export const numberArrayTypeInfo = numberArrayType; export const i32ArrayTypeInfo = i32ArrayType; export const stringArrayTypeInfo = stringArrayType; export const stringArrayStructTypeInfo = stringArrayStructType; export const stringrefArrayTypeInfo = stringrefArrayType; export const stringrefArrayStructTypeInfo = stringrefArrayStructType; export const boolArrayTypeInfo = boolArrayType; export const anyArrayTypeInfo = anyArrayType; export const objectStructTypeInfo = objectStructType; export const infcTypeInfo = infcType; export const arrayBufferTypeInfo = arrayBufferType; export const dataViewTypeInfo = dataViewType; export const numberArrayStructTypeInfo = numberArrayStructType; ================================================ FILE: src/backend/binaryen/glue/transform.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import binaryen from 'binaryen'; import * as binaryenCAPI from './binaryen.js'; import { ptrInfo, typeInfo } from './utils.js'; /** packed types */ export namespace Packed { export const Not: binaryenCAPI.PackedType = 0; export const I8: binaryenCAPI.PackedType = 1; export const I16: binaryenCAPI.PackedType = 2; } export namespace StringRefNewOp { export const UTF8 = 0; export const WTF8 = 1; export const WTF16 = 3; export const UTF8FromArray = 4; export const WTF8FromArray = 5; } export namespace StringRefMeatureOp { export const UTF8 = 0; export const WTF8 = 1; export const WTF16 = 2; } export namespace StringRefEqOp { export const EQ = 0; export const COMPARE = 1; } export namespace StringRefSliceOp { export const WTF8 = 0; export const WTF16 = 1; } export namespace StringRefAsOp { export const WTF8 = 0; export const WTF16 = 1; export const ITER = 2; } export function arrayToPtr(array: binaryen.ExpressionRef[]): ptrInfo { const arrLen = array.length; const ptrAddress = binaryenCAPI._malloc(arrLen << 2); let curElemAddress = ptrAddress; for (let i = 0; i < arrLen; i++) { const curArrayElem = array[i]; binaryenCAPI.__i32_store(curElemAddress, curArrayElem); curElemAddress += 4; } const ptrInfo: ptrInfo = { ptr: ptrAddress, len: arrLen, }; return ptrInfo; } export function ptrToArray(ptrInfo: ptrInfo): binaryen.ExpressionRef[] { const ptrAddress = ptrInfo.ptr; const arrLen = ptrInfo.len; const array: binaryen.ExpressionRef[] = []; let curElemAddress = ptrAddress; for (let i = 0; i < arrLen; i++) { const curArrayElem = binaryenCAPI.__i32_load(curElemAddress); array.push(curArrayElem); curElemAddress += 4; } binaryenCAPI._free(ptrAddress); return array; } function allocU32Array(u32Array: binaryenCAPI.u32[]): binaryenCAPI.usize { const arrLen = u32Array.length; const ptrAddress = binaryenCAPI._malloc(arrLen << 2); let curElemAddress = ptrAddress; for (let i = 0; i < arrLen; i++) { binaryenCAPI.__i32_store(curElemAddress, u32Array[i]); curElemAddress += 4; } return ptrAddress; } function allocU8Array(u8Array: boolean[]): binaryenCAPI.usize { const arrLen = u8Array.length; const ptrAddress = binaryenCAPI._malloc(arrLen); for (let i = 0; i < arrLen; i++) { const curArrayElem = u8Array[i] ? 1 : 0; binaryenCAPI.__i32_store8(ptrAddress + i, curArrayElem); } return ptrAddress; } export function initArrayType( elementType: binaryenCAPI.TypeRef, elementPackedType: binaryenCAPI.PackedType, elementMutable: binaryenCAPI.bool, nullable: binaryenCAPI.bool, buildIndex: number, tb: binaryenCAPI.TypeBuilderRef, ): typeInfo { binaryenCAPI._TypeBuilderSetArrayType( tb, buildIndex < 0 ? 0 : buildIndex, elementType, elementPackedType, elementMutable, ); if (buildIndex >= 0) { const heapType = binaryenCAPI._TypeBuilderGetTempHeapType( tb, buildIndex, ); const refType = binaryenCAPI._TypeBuilderGetTempRefType( tb, heapType, nullable, ); return { typeRef: refType, heapTypeRef: heapType }; } const builtHeapType: binaryenCAPI.HeapTypeRef[] = new Array(1); const builtHeapTypePtr = arrayToPtr(builtHeapType); binaryenCAPI._TypeBuilderBuildAndDispose(tb, builtHeapTypePtr.ptr, 0, 0); const arrayType = binaryenCAPI._BinaryenTypeFromHeapType( ptrToArray(builtHeapTypePtr)[0], nullable, ); const arrayRef = binaryenCAPI._BinaryenTypeGetHeapType(arrayType); const arrayTypeInfo: typeInfo = { typeRef: arrayType, heapTypeRef: arrayRef, }; return arrayTypeInfo; } /** Object */ export const emptyStructType = initStructType( [], [], [], 0, true, -1, binaryenCAPI._TypeBuilderCreate(1), ); /** vtable base type */ export const baseVtableType = initStructType( [binaryen.i32], [Packed.Not], [false], 1, true, -1, binaryenCAPI._TypeBuilderCreate(1), ); /** object base Type / interface Type */ export const baseStructType = initStructType( [baseVtableType.typeRef], [Packed.Not], [false], 1, true, -1, binaryenCAPI._TypeBuilderCreate(1), ); /** closure type ${${}, ${}, funcref}*/ export const builtinClosureType = initStructType( [ emptyStructType.typeRef, emptyStructType.typeRef, binaryenCAPI._BinaryenTypeFuncref(), ], [Packed.Not, Packed.Not, Packed.Not], [true, true, false], 3, true, -1, binaryenCAPI._TypeBuilderCreate(1), ); export function initStructType( fieldTypesList: Array, fieldPackedTypesList: Array, fieldMutablesList: Array, numFields: binaryenCAPI.i32, nullable: binaryenCAPI.bool, buildIndex: number, tb: binaryenCAPI.TypeBuilderRef, baseType?: binaryenCAPI.HeapTypeRef, ): typeInfo { const fieldTypes = arrayToPtr(fieldTypesList).ptr; const fieldPackedTypes = allocU32Array(fieldPackedTypesList); const fieldMutables = allocU8Array(fieldMutablesList); // const tb: binaryenCAPI.TypeBuilderRef = binaryenCAPI._TypeBuilderCreate(1); const index = buildIndex < 0 ? 0 : buildIndex; binaryenCAPI._TypeBuilderSetStructType( tb, index, fieldTypes, fieldPackedTypes, fieldMutables, numFields, ); binaryenCAPI._TypeBuilderSetOpen(tb, index); if (fieldTypesList.length > 0) { const subType = baseType ? baseType : emptyStructType.heapTypeRef; binaryenCAPI._TypeBuilderSetSubType(tb, index, subType); } if (buildIndex !== -1) { const heapType = binaryenCAPI._TypeBuilderGetTempHeapType(tb, index); const refType = binaryenCAPI._TypeBuilderGetTempRefType( tb, heapType, nullable, ); return { typeRef: refType, heapTypeRef: heapType }; } const builtHeapType: binaryenCAPI.HeapTypeRef[] = new Array(1); const builtHeapTypePtr = arrayToPtr(builtHeapType); binaryenCAPI._TypeBuilderBuildAndDispose(tb, builtHeapTypePtr.ptr, 0, 0); const structType = binaryenCAPI._BinaryenTypeFromHeapType( ptrToArray(builtHeapTypePtr)[0], nullable, ); const structRef = binaryenCAPI._BinaryenTypeGetHeapType(structType); const structTypeInfo: typeInfo = { typeRef: structType, heapTypeRef: structRef, }; return structTypeInfo; } /* GC Array */ /* array(i8) */ export const i8ArrayType = genarateI8ArrayTypeInfo(); /* array(f64) */ export const numberArrayType = genarateNumberArrayTypeInfo(); /* array(i32) */ export const i32ArrayType = genarateI32ArrayTypeInfo(); /* array(stringref) */ export const stringrefArrayType = genarateStringrefArrayTypeInfo(false); /* array(i32) */ export const boolArrayType = genarateBoolArrayTypeInfo(); /* array(anyref) */ export const anyArrayType = genarateAnyArrayTypeInfo(); /* GC Struct & GC Array */ /* struct(i32, array(i8)) */ export const stringType = generateStringTypeInfo(); /* array(struct(i32, array(i8))) */ export const stringArrayType = genarateStringArrayTypeInfo(false); /* struct(array(struct(i32, array(i8))), i32) */ export const stringArrayStructType = genarateStringArrayTypeInfo(true); /* GC Struct */ /* struct(array(stringref), i32) */ export const stringrefArrayStructType = genarateStringrefArrayTypeInfo(true); /* struct() */ export const objectStructType = emptyStructType; /* struct(struct(i32)) */ export const infcType = generateInfcTypeInfo(); /* struct(array(i8), i32) */ export const arrayBufferType = generateArrayStructTypeInfo(i8ArrayType); /* struct(struct(array(i8), i32), i32, i32) */ export const dataViewType = generateDataViewTypeInfo(); /* struct(array(f64), i32) */ export const numberArrayStructType = generateArrayStructTypeInfo(numberArrayType); export function generateArrayStructTypeInfo(arrayTypeInfo: typeInfo): typeInfo { const arrayStructTypeInfo = initStructType( [ binaryenCAPI._BinaryenTypeFromHeapType( arrayTypeInfo.heapTypeRef, true, ), binaryen.i32, ], [Packed.Not, Packed.Not], [true, true], 2, true, -1, binaryenCAPI._TypeBuilderCreate(1), ); return arrayStructTypeInfo; } export function generateArrayStructTypeForRec( arrayTypeInfo: typeInfo, buildIndex: number, tb: binaryenCAPI.TypeBuilderRef, ): typeInfo { const arrayStructTypeInfo = initStructType( [arrayTypeInfo.typeRef, binaryen.i32], [Packed.Not, Packed.Not], [true, true], 2, true, buildIndex, tb, ); return arrayStructTypeInfo; } // generate array type to store character context function genarateI8ArrayTypeInfo(): typeInfo { const charArrayTypeInfo = initArrayType( binaryen.i32, Packed.I8, true, true, -1, binaryenCAPI._TypeBuilderCreate(1), ); return charArrayTypeInfo; } // generate struct type to store string information function generateStringTypeInfo(): typeInfo { const charArrayTypeInfo = i8ArrayType; const stringTypeInfo = initStructType( [ binaryen.i32, binaryenCAPI._BinaryenTypeFromHeapType( charArrayTypeInfo.heapTypeRef, true, ), ], [Packed.Not, Packed.Not], [true, true], 2, true, -1, binaryenCAPI._TypeBuilderCreate(1), ); return stringTypeInfo; } // generate number array type function genarateNumberArrayTypeInfo(): typeInfo { const numberArrayTypeInfo = initArrayType( binaryen.f64, Packed.Not, true, true, -1, binaryenCAPI._TypeBuilderCreate(1), ); return numberArrayTypeInfo; } // generate i32 array type function genarateI32ArrayTypeInfo(): typeInfo { const i32ArrayTypeInfo = initArrayType( binaryen.i32, Packed.Not, true, true, -1, binaryenCAPI._TypeBuilderCreate(1), ); return i32ArrayTypeInfo; } // generate string array type function genarateStringArrayTypeInfo(struct_wrap: boolean): typeInfo { const stringTypeInfo = stringType; const stringArrayTypeInfo = initArrayType( stringTypeInfo.typeRef, Packed.Not, true, true, -1, binaryenCAPI._TypeBuilderCreate(1), ); if (struct_wrap) { return generateArrayStructTypeInfo(stringArrayTypeInfo); } return stringArrayTypeInfo; } function genarateStringrefArrayTypeInfo(struct_wrap: boolean): typeInfo { const stringArrayTypeInfo = initArrayType( binaryenCAPI._BinaryenTypeStringref(), Packed.Not, true, true, -1, binaryenCAPI._TypeBuilderCreate(1), ); if (struct_wrap) { return generateArrayStructTypeInfo(stringArrayTypeInfo); } return stringArrayTypeInfo; } function generateInfcTypeInfo(): typeInfo { return baseStructType; } // generate bool array type function genarateBoolArrayTypeInfo(): typeInfo { const boolArrayTypeInfo = initArrayType( binaryen.i32, Packed.Not, true, true, -1, binaryenCAPI._TypeBuilderCreate(1), ); return boolArrayTypeInfo; } // generate any array type function genarateAnyArrayTypeInfo(): typeInfo { const anyArrayTypeInfo = initArrayType( binaryenCAPI._BinaryenTypeAnyref(), Packed.Not, true, true, -1, binaryenCAPI._TypeBuilderCreate(1), ); return anyArrayTypeInfo; } function generateDataViewTypeInfo(): typeInfo { const dataViewTypeInfo = initStructType( [ binaryenCAPI._BinaryenTypeFromHeapType( arrayBufferType.heapTypeRef, true, ), binaryen.i32, binaryen.i32, ], [Packed.Not, Packed.Not, Packed.Not], [true, true, true], 3, true, -1, binaryenCAPI._TypeBuilderCreate(1), ); return dataViewTypeInfo; } export function createSignatureTypeRefAndHeapTypeRef( parameterTypes: Array, returnType: binaryenCAPI.TypeRef, buildIndex: number, tb: binaryenCAPI.TypeBuilderRef, ): typeInfo { const parameterLen = parameterTypes.length; const tempSignatureIndex = buildIndex < 0 ? 0 : buildIndex; let tempParamTypes = !parameterLen ? binaryen.none : parameterTypes[0]; if (parameterLen > 1) { const tempPtr = arrayToPtr(parameterTypes).ptr; tempParamTypes = binaryenCAPI._TypeBuilderGetTempTupleType( tb, tempPtr, parameterLen, ); binaryenCAPI._free(tempPtr); } binaryenCAPI._TypeBuilderSetSignatureType( tb, tempSignatureIndex, tempParamTypes, returnType, ); if (buildIndex !== -1) { const heapType = binaryenCAPI._TypeBuilderGetTempHeapType( tb, buildIndex, ); const refType = binaryenCAPI._TypeBuilderGetTempRefType( tb, heapType, true, ); return { typeRef: refType, heapTypeRef: heapType }; } const builtHeapType: binaryenCAPI.HeapTypeRef[] = new Array(1); const builtHeapTypePtr = arrayToPtr(builtHeapType); binaryenCAPI._TypeBuilderBuildAndDispose(tb, builtHeapTypePtr.ptr, 0, 0); const signatureType = binaryenCAPI._BinaryenTypeFromHeapType( ptrToArray(builtHeapTypePtr)[0], true, ); const signatureHeapType = binaryenCAPI._BinaryenTypeGetHeapType(signatureType); const signature: typeInfo = { typeRef: signatureType, heapTypeRef: signatureHeapType, }; return signature; } ================================================ FILE: src/backend/binaryen/glue/utils.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import * as binaryenCAPI from './binaryen.js'; export const GLOBAL_INIT_FUNC = 'global_init'; export interface ptrInfo { ptr: number; len: number; } export interface typeInfo { typeRef: binaryenCAPI.TypeRef; heapTypeRef: binaryenCAPI.HeapTypeRef; } ================================================ FILE: src/backend/binaryen/index.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import binaryen from 'binaryen'; import * as binaryenCAPI from './glue/binaryen.js'; import { arrayToPtr, emptyStructType } from './glue/transform.js'; import { PredefinedTypeId, Stack, isExportComment, isImportComment, isNativeSignatureComment, } from '../../utils.js'; import { importAnyLibAPI, importInfcLibAPI, generateGlobalContext, addItableFunc, generateGlobalJSObject, generateExtRefTableMaskArr, generateDynContext, importMemoryAPI, } from './lib/env_init.js'; import { WASMTypeGen } from './wasm_type_gen.js'; import { WASMExpressionGen } from './wasm_expr_gen.js'; import { WASMStatementGen } from './wasm_stmt_gen.js'; import { initGlobalOffset, initDefaultMemory, initDefaultTable, } from './memory.js'; import { BuiltinNames } from '../../../lib/builtin/builtin_name.js'; import { Ts2wasmBackend, ParserContext, DataSegmentContext } from '../index.js'; import { Logger } from '../../log.js'; import { callBuiltInAPIs } from './lib/init_builtin_api.js'; import { BlockNode, CaseClauseNode, DefaultClauseNode, ForInNode, ForNode, ForOfNode, FunctionOwnKind, IfNode, ModuleNode, NativeSignature, SemanticsNode, SwitchNode, TryNode, VarDeclareNode, WhileNode, } from '../../semantics/semantics_nodes.js'; import { BuildModuleNode } from '../../semantics/index.js'; import { ClosureContextType, ObjectType, Primitive, ValueType, ValueTypeKind, } from '../../semantics/value_types.js'; import { FunctionDeclareNode } from '../../semantics/semantics_nodes.js'; import { FunctionalFuncs, ItableFlag, META_INDEX_MASK, META_FLAG_MASK, SourceMapLoc, BackendLocalVar, UtilFuncs, NativeSignatureConversion, } from './utils.js'; import { MemberDescription, MemberType, ObjectDescription, } from '../../semantics/runtime.js'; import { dyntype } from './lib/dyntype/utils.js'; import { assert } from 'console'; import ts from 'typescript'; import { VarValue } from '../../semantics/value.js'; import { ValidateError } from '../../error.js'; import { getConfig } from '../../../config/config_mgr.js'; export class WASMFunctionContext { private binaryenCtx: WASMGen; private funcOpcodeArray: Array; private opcodeArrayStack = new Stack>(); private returnOpcode: binaryen.ExpressionRef; private returnIndex = 0; private currentFunc: FunctionDeclareNode; private varsTypeRef: Array = []; private hasGenerateVarsTypeRefs = false; private tmpBackendVars: Array = []; private _sourceMapLocs: SourceMapLoc[] = []; public localVarIdxNameMap = new Map(); constructor(binaryenCtx: WASMGen, func: FunctionDeclareNode) { this.binaryenCtx = binaryenCtx; this.funcOpcodeArray = new Array(); this.opcodeArrayStack.push(this.funcOpcodeArray); this.returnOpcode = this.binaryenCtx.module.return(); this.currentFunc = func; } i32Local() { return this.insertTmpVar(binaryen.i32); } insert(insn: binaryen.ExpressionRef) { this.opcodeArrayStack.peek().push(insn); } setReturnOpcode(returnOpcode: binaryen.ExpressionRef) { this.returnOpcode = returnOpcode; } get returnOp() { return this.returnOpcode; } get sourceMapLocs() { return this._sourceMapLocs; } enterScope() { this.opcodeArrayStack.push(new Array()); } exitScope() { const topMostArray = this.opcodeArrayStack.pop(); return topMostArray; } getBody() { return this.funcOpcodeArray; } get returnIdx() { return this.returnIndex; } insertReturnVar(returnVarType: binaryenCAPI.TypeRef) { const returnVarIdx = this.allocateTmpVarIdx(); this.returnIndex = returnVarIdx; const returnVar = { index: returnVarIdx, type: returnVarType, }; this.tmpBackendVars.push(returnVar); } insertTmpVar(tmpVarType: binaryenCAPI.TypeRef) { const tmpVarIdx = this.allocateTmpVarIdx(); const tmpVar = { index: tmpVarIdx, type: tmpVarType, }; this.tmpBackendVars.push(tmpVar); return tmpVar; } private generateFuncVarsTypeRefs(varNode: SemanticsNode) { if (varNode instanceof FunctionDeclareNode) { /* funtion vars */ if (varNode.varList) { for (const variable of varNode.varList) { const backendVar = { type: this.binaryenCtx.wasmTypeComp.getWASMValueType( variable.type, ), index: variable.index, }; this.varsTypeRef.push(backendVar); this.setLocalVarName(variable.name, variable.index); } } this.generateFuncVarsTypeRefs(varNode.body); } else if (varNode instanceof BlockNode) { /* block vars */ if (varNode.varList) { for (const variable of varNode.varList) { const backendVar = { type: this.binaryenCtx.wasmTypeComp.getWASMValueType( variable.type, ), index: variable.index, }; this.varsTypeRef.push(backendVar); this.setLocalVarName(variable.name, variable.index); } } varNode.statements.forEach((s) => { this.generateFuncVarsTypeRefs(s); }); } else if ( varNode instanceof ForNode || varNode instanceof ForInNode || varNode instanceof ForOfNode || varNode instanceof WhileNode || varNode instanceof CaseClauseNode || varNode instanceof DefaultClauseNode || varNode instanceof TryNode ) { if (varNode.body instanceof BlockNode) { this.generateFuncVarsTypeRefs(varNode.body); } if (varNode instanceof TryNode) { if (varNode.catchClause) { this.generateFuncVarsTypeRefs(varNode.catchClause.body); } if (varNode.finallyBlock) { this.generateFuncVarsTypeRefs(varNode.finallyBlock); } } } else if (varNode instanceof SwitchNode) { varNode.caseClause.forEach((c) => { this.generateFuncVarsTypeRefs(c); }); if (varNode.defaultClause) { this.generateFuncVarsTypeRefs(varNode.defaultClause); } } else if (varNode instanceof IfNode) { if (varNode.trueNode) { this.generateFuncVarsTypeRefs(varNode.trueNode); } if (varNode.falseNode) { this.generateFuncVarsTypeRefs(varNode.falseNode); } } } getFuncVarsTypeRefs(varNode: SemanticsNode) { if (!this.hasGenerateVarsTypeRefs) { this.generateFuncVarsTypeRefs(varNode); this.hasGenerateVarsTypeRefs = true; } return this.varsTypeRef; } allocateTmpVarIdx() { const allFuncVarsLen = this.getFuncVarsTypeRefs( this.currentFunc, ).length; const allFuncParamsLen = (this.currentFunc.parameters ? this.currentFunc.parameters.length : 0) + this.currentFunc.funcType.envParamLen; return allFuncParamsLen + allFuncVarsLen + this.tmpBackendVars.length; } getAllFuncVarsTypeRefs() { const funcVarsTypeRefs = this.getFuncVarsTypeRefs(this.currentFunc); const backendVarsTypeRefs: BackendLocalVar[] = []; for (const value of this.tmpBackendVars) { backendVarsTypeRefs.push(value); this.setLocalVarName('tempVar', value.index); } return funcVarsTypeRefs.concat(backendVarsTypeRefs); } setLocalVarName(name: string, index: number) { if (this.localVarIdxNameMap.has(name)) { this.localVarIdxNameMap.set(`${name}_${index}`, index); } else { this.localVarIdxNameMap.set(name, index); } } } export class WASMGen extends Ts2wasmBackend { private _semanticModule: ModuleNode; private _binaryenModule: binaryen.Module; private _wasmTypeCompiler; private _wasmExprCompiler; private _wasmStmtCompiler; currentFuncCtx?: WASMFunctionContext; dataSegmentContext?: DataSegmentContext; public globalInitFuncCtx: WASMFunctionContext; public globalInitArray: Array = []; private debugFileIndex = new Map(); /** source map file url */ private map: string | null = null; public generatedFuncNames: Array = []; public sourceFileLists: ts.SourceFile[] = []; public emptyRef: binaryen.ExpressionRef; constructor(parserContext: ParserContext) { super(parserContext); if (getConfig().debug) { binaryen.setDebugInfo(true); } this.sourceFileLists = parserContext.sourceFileLists; this._semanticModule = BuildModuleNode(parserContext); this._binaryenModule = new binaryen.Module(); this._wasmTypeCompiler = new WASMTypeGen(this); this._wasmExprCompiler = new WASMExpressionGen(this); this._wasmStmtCompiler = new WASMStatementGen(this); this.dataSegmentContext = new DataSegmentContext(); this.globalInitFuncCtx = new WASMFunctionContext( this, this._semanticModule.globalInitFunc!, ); this.emptyRef = FunctionalFuncs.getEmptyRef(this._binaryenModule); } get module(): binaryen.Module { return this._binaryenModule; } get wasmTypeComp(): WASMTypeGen { return this._wasmTypeCompiler; } get wasmExprComp(): WASMExpressionGen { return this._wasmExprCompiler; } get semanticModule() { return this._semanticModule; } public hasFuncName(funcName: string) { return this.generatedFuncNames.find((elem) => { return funcName === elem; }); } public codegen(options?: any): void { binaryen.setDebugInfo(getConfig().debug); this._binaryenModule.setFeatures(binaryen.Features.All); this._binaryenModule.autoDrop(); this.wasmGenerate(); /* Sometimes binaryen can't generate binary module, we dump the module to text and load it back. This is just a simple workaround, we need to find out the root cause */ const textModule = this._binaryenModule.emitText(); this._binaryenModule.dispose(); try { this._binaryenModule = binaryen.parseText(textModule); } catch (e) { Logger.debug(textModule); Logger.debug(e); Logger.error(`Generated module is invalid`); throw e; } this._binaryenModule.setFeatures(binaryen.Features.All); this._binaryenModule.autoDrop(); if (getConfig().opt > 0) { binaryenCAPI._BinaryenSetOptimizeLevel(getConfig().opt); binaryenCAPI._BinaryenSetShrinkLevel(0); binaryenCAPI._BinaryenModuleOptimize(this._binaryenModule.ptr); } const validationResult = this._binaryenModule.validate(); if (validationResult === 0) { Logger.error(`Validation wasm module failed`); throw new ValidateError('Failed to validate generated wasm module'); } } public emitBinary(options?: any): Uint8Array { let res: Uint8Array = this._binaryenModule.emitBinary(); if (getConfig().sourceMap) { const name = `${options.name_prefix}.wasm.map`; const binaryInfo = this._binaryenModule.emitBinary(name); res = binaryInfo.binary; this.map = binaryInfo.sourceMap; } return res; } public emitText(options?: any): string { if (options?.format === 'Stack-IR') { return this._binaryenModule.emitStackIR(); } return this._binaryenModule.emitText(); } public emitSourceMap(name: string): string { /** generete source map file */ if (this.map === null) { return ''; } const sourceMapStr = this.map; const content = JSON.parse(sourceMapStr); content.sourceRoot = `./${name}`; const sourceCode: string[] = []; for (const sourceFile of this.sourceFileLists) { if (this.debugFileIndex.has(sourceFile.fileName)) { sourceCode.push(sourceFile.getFullText()); } } content.sourcesContent = sourceCode; this.map = null; return JSON.stringify(content); } public dispose(): void { this._binaryenModule.dispose(); } private wasmGenerate() { UtilFuncs.clearWasmStringMap(); FunctionalFuncs.resetDynContextRef(); initDefaultTable(this.module); /* init builtin APIs */ callBuiltInAPIs(this.module); /* init any lib APIs */ importAnyLibAPI(this.module); this.globalInitFuncCtx.insert(generateDynContext(this.module)); /* init interface lib APIs */ importInfcLibAPI(this.module); /* init libc builtin APIs */ importMemoryAPI(this.module); addItableFunc(this.module); if (getConfig().enableException) { /* add exception tags: anyref */ this.module.addTag( BuiltinNames.errorTag, binaryen.anyref, binaryen.none, ); this.module.addTag( BuiltinNames.finallyTag, binaryen.anyref, binaryen.none, ); } /** parse all recursive types firstly */ this.wasmTypeComp.parseCircularRecType(); /* add global vars */ this.addGlobalVars(); /* parse functions */ this.parseFuncs(); generateGlobalContext(this.module); generateExtRefTableMaskArr(this.module); BuiltinNames.JSGlobalObjects.forEach((key) => { generateGlobalJSObject(this.module, key); /* Insert at the second slot (right after dyntype context initialized) */ this.globalInitFuncCtx.insert(this.genrateInitJSGlobalObject(key)); BuiltinNames.JSGlobalObjects.delete(key); }); const segments = []; const segmentInfo = this.dataSegmentContext!.generateSegment(); if (segmentInfo) { segments.push({ offset: this.module.i32.const(segmentInfo!.offset), data: segmentInfo!.data, passive: false, }); } /* init wasm global memory value */ initDefaultMemory(this.module, segments); initGlobalOffset(this.module, this.dataSegmentContext!.currentOffset); this.initEnv(); } private addGlobalVars() { /* all global vars will be put into global init function, all mutable */ const globalVarArray = this._semanticModule.globalVars; for (const globalVar of globalVarArray) { if (globalVar.name.includes('builtin')) { continue; } this.module.removeGlobal(globalVar.name); /* get wasm type */ const varTypeRef = this.wasmTypeComp.getWASMValueType( globalVar.type, ); /* TODO: it seems that isDeclare information not recorded. flag? */ /* get the default value based on type */ this.module.addGlobal( globalVar.name, varTypeRef, true, FunctionalFuncs.getVarDefaultValue(this.module, globalVar.type), ); } } /* parse functions */ private parseFuncs() { const funcArray = this._semanticModule!.functions; for (const func of funcArray) { this.parseFunc(func); } } public parseFunc(func: FunctionDeclareNode) { if (this.hasFuncName(func.name)) { return; } if ((func.ownKind & FunctionOwnKind.DECORATOR) !== 0) { /* Function with @binaryen decorator is implemented directly using binaryen API, don't generate code for them */ return; } /* get function type */ const tsFuncType = func.funcType; const paramWASMTypes = this.wasmTypeComp.getWASMFuncParamTypes(tsFuncType); const returnType = tsFuncType.returnType; let returnWASMType = this.wasmTypeComp.getWASMValueType(returnType); const oriParamWasmTypes = this.wasmTypeComp.getWASMFuncOriParamTypes(tsFuncType); /* generate import function name */ const levelNames = func.name.split(BuiltinNames.moduleDelimiter); let funcPureName = levelNames[levelNames.length - 1]; if ((func.ownKind & FunctionOwnKind.METHOD) !== 0) { funcPureName = `${ levelNames[levelNames.length - 2] }_${funcPureName}`; } let importName = funcPureName; let exportName = funcPureName; /** declare functions */ if ((func.ownKind & FunctionOwnKind.DECLARE) !== 0) { let skipEnvParamLen = BuiltinNames.envParamLen; if ((func.ownKind & FunctionOwnKind.METHOD) !== 0) { /* For method, just skip the @context */ skipEnvParamLen = BuiltinNames.envParamLen - 1; } let calledParamValueRefs: binaryen.ExpressionRef[] = []; for (let i = skipEnvParamLen; i < paramWASMTypes.length; i++) { calledParamValueRefs.push( this.module.local.get(i, paramWASMTypes[i]), ); } const internalFuncName = `${func.name}${BuiltinNames.declareSuffix}`; let moduleName = BuiltinNames.externalModuleName; let importParamTypeRefs = paramWASMTypes.slice(skipEnvParamLen); const innerOpStmts: binaryen.ExpressionRef[] = []; const vars: binaryen.Type[] = []; const mallocOffsets: binaryen.ExpressionRef[] = []; for (let comment of func.comments) { if (isImportComment(comment)) { moduleName = comment.moduleName; importName = comment.funcName; } else if (isNativeSignatureComment(comment)) { comment = comment as NativeSignature; importParamTypeRefs = []; calledParamValueRefs = []; returnWASMType = this.wasmTypeComp.getWASMValueType( comment.returnType, ); for (const paramType of comment.paramTypes) { const paramTypeRef = this.wasmTypeComp.getWASMValueType(paramType); importParamTypeRefs.push(paramTypeRef); } FunctionalFuncs.parseNativeSignature( this.module, innerOpStmts, tsFuncType.argumentsType, paramWASMTypes.slice(skipEnvParamLen), comment.paramTypes, skipEnvParamLen, calledParamValueRefs, vars, mallocOffsets, true, ); } else if (isExportComment(comment)) { exportName = comment.exportName; } } this.module.addFunctionImport( internalFuncName, moduleName, importName, binaryen.createType(importParamTypeRefs), returnWASMType, ); const callOp = this.module.call( internalFuncName, calledParamValueRefs, returnWASMType, ); // TODO: ts's return type is not same with native signature's return type. if (returnType.kind !== ValueTypeKind.VOID) { innerOpStmts.push(this.module.return(callOp)); } else { innerOpStmts.push(callOp); } for (const mallocOffset of mallocOffsets) { innerOpStmts.push( this.module.call( BuiltinNames.freeFunc, [mallocOffset], binaryen.none, ), ); } this.module.addFunction( func.name, binaryen.createType(paramWASMTypes), returnWASMType, vars, this.module.block(null, innerOpStmts, returnWASMType), ); if ((func.ownKind & FunctionOwnKind.EXPORT) !== 0) { this.module.addFunctionExport(internalFuncName, exportName); } return; } /* use WASMFunctionContext to record information */ this.currentFuncCtx = new WASMFunctionContext(this, func); /* the calculation of closureContext value is moved to semantic tree and is a statement in body */ /* assign value for function's context variable */ if (func.varList && func.varList[0].initCtx) { const freeVars: VarDeclareNode[] = []; if (func.parameters) { for (const p of func.parameters) { if (p.closureIndex !== undefined) { freeVars.push(p); } } } for (const v of func.varList) { if (v.closureIndex !== undefined) { freeVars.push(v); } } this.assignCtxVar(func.varList[0], freeVars); } /* assign value for method's this variable */ if ( func.varList && (func.ownKind & FunctionOwnKind.METHOD) !== 0 && (func.ownKind & FunctionOwnKind.STATIC) === 0 ) { if (func.varList[1] && func.varList[1].name === 'this') { this.assignThisVar(func.varList[1]); } } /* add return value iff return type is not void, must ahead of parse return Statement */ if (returnType.kind !== ValueTypeKind.VOID) { this.currentFuncCtx.insertReturnVar( this.wasmTypeComp.getWASMValueType(returnType), ); } /* for start function, need to call import start funcs */ if (func.importStartFuncNameList) { for (const importStartFuncName of func.importStartFuncNameList) { this.currentFuncCtx.insert( this.module.call(importStartFuncName, [], binaryen.none), ); } } // manually add SUPER() for ctor should before parseBody() /** insert SUPER() for class which haven't declare constructor and is sub class*/ if ( levelNames[levelNames.length - 1] === 'constructor' && func.varList && !!(func.ownKind & FunctionOwnKind.METHOD) && !(func.ownKind & FunctionOwnKind.STATIC) ) { const meta = func.thisClassType!.meta; const ctor = meta.ctor; const base = meta.base; const args: binaryen.ExpressionRef[] = []; if (ctor && base && base.ctor) { const baseClassCtor = base.name.substring(1) + '|constructor'; if (!ctor.isDeclaredCtor) { args.push(this.emptyRef); args.push( this.module.local.get( func.varList[1].index, emptyStructType.typeRef, ), ); if (func.parameters) { for (const arg of func.parameters) { args.push( this.module.local.get( arg.index, this.wasmTypeComp.getWASMValueType( arg.type, ), ), ); } } this.currentFuncCtx.insert( this.module.drop( this.module.call( baseClassCtor, args, binaryen.none, ), ), ); } } } this.parseBody(func.body); if (!(func.ownKind & FunctionOwnKind.START)) { this.currentFuncCtx.localVarIdxNameMap.set('@context', 0); this.currentFuncCtx.localVarIdxNameMap.set('@this', 1); } if (func.parameters) { for (const p of func.parameters) { /** must no duplicate parameter name here */ this.currentFuncCtx.localVarIdxNameMap.set(p.name, p.index); } } /* get all vars wasm types, must behind the parseBody */ const backendLocalVars = this.currentFuncCtx.getAllFuncVarsTypeRefs(); /** sort the local variables array by index */ backendLocalVars.sort((a, b) => { return a.index - b.index; }); const allVarsTypeRefs = backendLocalVars.map((value) => value.type); /* For class's constructor, should assign to return idx manually */ if ( levelNames[levelNames.length - 1] === 'constructor' && func.varList && (func.ownKind & FunctionOwnKind.METHOD) !== 0 && (func.ownKind & FunctionOwnKind.STATIC) === 0 ) { const thisVar = func.varList[1]; const thisTypeRef = this.wasmTypeComp.getWASMValueType( thisVar.type, ); const getThisVar = this.module.local.get( thisVar.index, thisTypeRef, ); const assignRef = this.module.local.set( this.currentFuncCtx.returnIdx, getThisVar, ); this.currentFuncCtx.insert(assignRef); } const bodyRef = this.module.block( 'statements', this.currentFuncCtx.getBody(), ); /* add return statement */ if (returnType.kind !== ValueTypeKind.VOID) { const returnValue = this.module.local.get( this.currentFuncCtx.returnIdx, returnWASMType, ); this.currentFuncCtx.setReturnOpcode( this.module.return(returnValue), ); } if ( func.isInEnterScope && (func.ownKind & FunctionOwnKind.START) !== 0 ) { /* set enter module start function as wasm start function */ const startFuncStmts: binaryen.ExpressionRef[] = []; /* call globalInitFunc */ startFuncStmts.push( this.module.call( BuiltinNames.globalInitFuncName, [], binaryen.none, ), ); startFuncStmts.push(this.module.call(func.name, [], binaryen.none)); const startFuncRef = this.module.addFunction( BuiltinNames.start, binaryen.none, binaryen.none, [], this.module.block(null, startFuncStmts), ); this.module.addFunctionExport( BuiltinNames.start, getConfig().entry, ); if (getConfig().startSection) { this.module.setStart(startFuncRef); } } let funcRef: binaryen.FunctionRef; if (this.wasmTypeComp.heapType.has(func.funcType)) { const heap = this.wasmTypeComp.getWASMHeapType(func.funcType); funcRef = binaryenCAPI._BinaryenAddFunctionWithHeapType( this.module.ptr, UtilFuncs.getCString(func.name), heap, arrayToPtr(allVarsTypeRefs).ptr, allVarsTypeRefs.length, this.module.block( null, [bodyRef, this.currentFuncCtx.returnOp], returnWASMType, ), ); } else { funcRef = this.module.addFunction( func.name, binaryen.createType(paramWASMTypes), returnWASMType, allVarsTypeRefs, this.module.block( null, [bodyRef, this.currentFuncCtx.returnOp], returnWASMType, ), ); } if (getConfig().debug) { this.setDebugLocation(funcRef, func.debugFilePath); } this.currentFuncCtx.localVarIdxNameMap.clear(); /** wrapped export functions */ if ( (func.ownKind & (FunctionOwnKind.EXPORT | FunctionOwnKind.DEFAULT)) === (FunctionOwnKind.EXPORT | FunctionOwnKind.DEFAULT) ) { if ( func.isInEnterScope || func.comments.some((comment) => { return isExportComment(comment); }) ) { const exportWrapperName = funcPureName.concat( BuiltinNames.wrapperSuffix, ); let exportName = funcPureName; let exportParamTypeRefs = oriParamWasmTypes; const calledParamValueRefs: binaryen.ExpressionRef[] = []; for (let i = 0; i < tsFuncType.envParamLen; i++) { calledParamValueRefs.push(this.emptyRef); } for (let i = 0; i < exportParamTypeRefs.length; i++) { calledParamValueRefs.push( this.module.local.get(i, exportParamTypeRefs[i]), ); } const innerOpStmts: binaryen.ExpressionRef[] = []; const vars: binaryen.Type[] = []; const mallocOffsets: binaryen.ExpressionRef[] = []; for (let comment of func.comments) { if (isExportComment(comment)) { exportName = comment.exportName; } else if (isNativeSignatureComment(comment)) { comment = comment as NativeSignature; exportParamTypeRefs = []; calledParamValueRefs.splice(tsFuncType.envParamLen); returnWASMType = this.wasmTypeComp.getWASMValueType( comment.returnType, ); for (const paramType of comment.paramTypes) { const paramTypeRef = this.wasmTypeComp.getWASMValueType(paramType); exportParamTypeRefs.push(paramTypeRef); } FunctionalFuncs.parseNativeSignature( this.module, innerOpStmts, comment.paramTypes, exportParamTypeRefs, tsFuncType.argumentsType, tsFuncType.envParamLen, calledParamValueRefs, vars, mallocOffsets, false, ); } } innerOpStmts.push( this.module.call( BuiltinNames.globalInitFuncName, [], binaryen.none, ), ); const callOp = this.module.call( func.name, calledParamValueRefs, returnWASMType, ); // TODO: ts's return type is not same with native signature's return type. if (returnType.kind !== ValueTypeKind.VOID) { innerOpStmts.push(this.module.return(callOp)); } else { innerOpStmts.push(callOp); } for (const mallocOffset of mallocOffsets) { innerOpStmts.push( this.module.call( BuiltinNames.freeFunc, [mallocOffset], binaryen.none, ), ); } this.module.addFunction( exportWrapperName, binaryen.createType(exportParamTypeRefs), returnWASMType, vars, this.module.block(null, innerOpStmts), ); this.module.addFunctionExport(exportWrapperName, exportName); } } this.generatedFuncNames.push(func.name); } public assignCtxVar(context: VarDeclareNode, freeVars: VarDeclareNode[]) { const assignedCtxVar = context; const assignedCtxTypeRef = this.wasmTypeComp.getWASMHeapType( assignedCtxVar.type, ); const initCtxVar = context.initCtx!; const initCtxTypeRef = this.wasmTypeComp.getWASMValueType( initCtxVar.type, ); const initCtxVarRef = binaryenCAPI._BinaryenRefCast( this.module.ptr, this.module.local.get(initCtxVar.index, emptyStructType.typeRef), initCtxTypeRef, ); let assignRef: binaryen.ExpressionRef; /** the function or block generate free variables */ if (freeVars.length > 0) { const freeVarList: binaryen.ExpressionRef[] = []; freeVarList.push(initCtxVarRef); for (const f of freeVars) { let value = this.module.local.get( f.index, this.wasmTypeComp.getWASMValueType(f.type), ); /** if 'this' as free variable */ if (f.name === 'this') { const type = this.wasmTypeComp.getWASMValueType(f.type); // parameter `this` index const thisParamIndex = 1; value = binaryenCAPI._BinaryenRefCast( this.module.ptr, this.module.local.get( thisParamIndex, emptyStructType.typeRef, ), type, ); } freeVarList.push(value); } const newCtxStruct = binaryenCAPI._BinaryenStructNew( this.module.ptr, arrayToPtr(freeVarList).ptr, freeVarList.length, assignedCtxTypeRef, ); assignRef = this.module.local.set( assignedCtxVar.index, newCtxStruct, ); } else { assignRef = this.module.local.set( assignedCtxVar.index, initCtxVarRef, ); } this.currentFuncCtx!.insert(assignRef); } public assignThisVar(thisVar: VarDeclareNode) { const initedThisVarIdx = 1; const assignedThisTypeRef = this.wasmTypeComp.getWASMValueType( thisVar.type, ); const initCtxVarRef = binaryenCAPI._BinaryenRefCast( this.module.ptr, this.module.local.get(initedThisVarIdx, emptyStructType.typeRef), assignedThisTypeRef, ); const assignRef = this.module.local.set(thisVar.index, initCtxVarRef); this.currentFuncCtx!.insert(assignRef); } /* parse function body */ private parseBody(body: BlockNode) { /* assign value for block's context variable */ if ( body.varList && body.varList[0].type instanceof ClosureContextType && body.varList[0].initCtx ) { const freeVars: VarDeclareNode[] = []; for (const v of body.varList) { if (v.closureIndex !== undefined) { freeVars.push(v); } } this.assignCtxVar(body.varList[0], freeVars); } for (const stmt of body.statements) { const stmtRef = this._wasmStmtCompiler.WASMStmtGen(stmt); this.currentFuncCtx!.insert(stmtRef); } } private initEnv() { const backendLocalVars = this.globalInitFuncCtx.getAllFuncVarsTypeRefs(); /** sort the local variables array by index */ backendLocalVars.sort((a, b) => { return a.index - b.index; }); const allVarsTypeRefs = backendLocalVars.map((value) => value.type); this.module.addFunction( BuiltinNames.globalInitFuncName, binaryen.none, binaryen.none, allVarsTypeRefs, this.module.block(null, this.globalInitFuncCtx.exitScope()), ); } public generateRawString(str: string): number { const offset = this.dataSegmentContext!.addString(str); return offset; } public generateMetaInfo(objType: ObjectType): number { if (this.dataSegmentContext!.metaMap.has(objType.typeId)) { return this.dataSegmentContext!.metaMap.get(objType.typeId)!; } const members = objType.meta.members; let dataLength = members.length; dataLength += members.filter((m) => m.hasSetter && m.hasGetter).length; const buffer = new Uint32Array(3 + 3 * dataLength); buffer[0] = objType.typeId; buffer[1] = objType.implId; buffer[2] = dataLength; // if (buffer[1] > (1 << 27) - 1) { // throw new Error('Too many members in object type'); // } let memberMethodsCnt = 1; const cnt = Math.min(dataLength, members.length); let memberFieldsCnt = 1; // In obj, the first field is vtable. for (let i = 0, j = 3; i < cnt; i++, j += 3) { const member = members[i]; const memberName = member.name; buffer[j] = this.generateRawString(memberName); if (member.type === MemberType.FIELD) { const flag = ItableFlag.FIELD; const index = memberFieldsCnt++; buffer[j + 1] = (flag & META_FLAG_MASK) | ((index << 4) & META_INDEX_MASK); buffer[j + 2] = FunctionalFuncs.getPredefinedTypeId( member.valueType, ); } else if (member.type === MemberType.METHOD) { const flag = ItableFlag.METHOD; const index = memberMethodsCnt++; buffer[j + 1] = (flag & META_FLAG_MASK) | ((index << 4) & META_INDEX_MASK); buffer[j + 2] = FunctionalFuncs.getPredefinedTypeId( member.valueType, ); } else if (member.type === MemberType.ACCESSOR) { if (member.hasGetter) { const flag = ItableFlag.GETTER; const index = memberMethodsCnt++; buffer[j + 1] = (flag & META_FLAG_MASK) | ((index << 4) & META_INDEX_MASK); buffer[j + 2] = FunctionalFuncs.getPredefinedTypeId( (member.getter as VarValue).type, ); } if (member.hasGetter && member.hasSetter) { j += 3; buffer[j] = buffer[j - 3]; } if (member.hasSetter) { const flag = ItableFlag.SETTER; const index = memberMethodsCnt++; buffer[j + 1] = (flag & META_FLAG_MASK) | ((index << 4) & META_INDEX_MASK); buffer[j + 2] = FunctionalFuncs.getPredefinedTypeId( (member.setter as VarValue).type, ); } } } const offset = this.dataSegmentContext!.addData( new Uint8Array(buffer.buffer), ); this.dataSegmentContext!.metaMap.set(objType.typeId, offset); return offset; } public findMethodImplementClass( meta: ObjectDescription, member: MemberDescription, ): ObjectDescription | undefined { if (member.isOwn) { return meta; } let curMeta = meta.base; while (curMeta) { if (curMeta.findMember(member.name)?.isOwn) { return curMeta; } curMeta = curMeta.base; } return undefined; } public getMethodMangledName( member: MemberDescription, meta: ObjectDescription, accessorKind?: number /* 0 is getter, 1 is setter */, ) { const implClassMeta = this.findMethodImplementClass(meta, member); assert(implClassMeta, 'implClassMeta should not be undefined'); let methodName = member.name; let implClassName = implClassMeta!.name; /** * the ACCESSOR member contains both getter and setter. * getter and setter can be implemented in different classes with inheritance relationships. */ if (accessorKind !== undefined) { if (accessorKind === 0) { methodName = 'get_'.concat(member.name); if (member.methodOrAccessor && member.methodOrAccessor.getter) { const getterValue = member.methodOrAccessor .getter as VarValue; const getter = getterValue.ref as FunctionDeclareNode; implClassName = getter.thisClassType!.meta.name; } } else if (accessorKind === 1) { methodName = 'set_'.concat(member.name); if (member.methodOrAccessor && member.methodOrAccessor.setter) { const setterValue = member.methodOrAccessor .setter as VarValue; const setter = setterValue.ref as FunctionDeclareNode; implClassName = setter.thisClassType!.meta.name; } } } if (implClassName.includes('@')) { implClassName = implClassName.slice(1); } return UtilFuncs.getFuncName(implClassName, methodName); } public genrateInitJSGlobalObject(name: string) { const namePointer = this.generateRawString(name); const JSGlobalObj = this.module.call( dyntype.dyntype_get_global, [ this.module.global.get( dyntype.dyntype_context, dyntype.dyn_ctx_t, ), this.module.i32.const(namePointer), ], dyntype.dyn_value_t, ); const expr = binaryenCAPI._BinaryenGlobalSet( this.module.ptr, UtilFuncs.getCString(name), JSGlobalObj, ); return expr; } private setDebugLocation( funcRef: binaryen.FunctionRef, debugFilePath: string, ) { const localNameMap = this.currentFuncCtx!.localVarIdxNameMap; localNameMap.forEach((idx, name) => { binaryenCAPI._BinaryenFunctionSetLocalName( funcRef, idx, UtilFuncs.getCString(name), ); }); const isBuiltIn = debugFilePath.includes(BuiltinNames.builtinTypeName); if (isBuiltIn) { return; } // add debug location if (!this.debugFileIndex.has(debugFilePath)) { this.debugFileIndex.set( debugFilePath, this.module.addDebugInfoFileName(debugFilePath), ); } const fileIndex = this.debugFileIndex.get(debugFilePath)!; /** set source mapping locations*/ const sourceMap = this.currentFuncCtx!.sourceMapLocs; for (let i = 0; i < sourceMap.length; i++) { const loc = sourceMap[i]; this.module.setDebugLocation( funcRef, loc.ref, fileIndex, loc.location.line, loc.location.character, ); } } } ================================================ FILE: src/backend/binaryen/lib/array_utils.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import binaryen from 'binaryen'; import * as binaryenCAPI from '../glue/binaryen.js'; export function array_get_length_i32( module: binaryen.Module, arr: binaryen.ExpressionRef, ) { return binaryenCAPI._BinaryenStructGet( module.ptr, 1, arr, binaryen.getExpressionType(arr), false, ); } export function array_get_data( module: binaryen.Module, arr: binaryen.ExpressionRef, ) { return binaryenCAPI._BinaryenStructGet( module.ptr, 0, arr, binaryen.getExpressionType(arr), false, ); } ================================================ FILE: src/backend/binaryen/lib/dyntype/utils.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import binaryen from 'binaryen'; import * as binaryenCAPI from '../../glue/binaryen.js'; import { getConfig } from '../../../../../config/config_mgr.js'; import { stringTypeInfo } from '../../glue/packType.js'; export namespace dyntype { // export global dyntype context variable name export const dyntype_context = 'dyntype_context'; // export module name export const module_name = 'libdyntype'; // export type export const dyn_ctx_t = binaryen.anyref; export const dyn_value_t = binaryen.anyref; export const dyn_type_t = binaryen.i32; export const cvoid = binaryen.none; export const double = binaryen.f64; export const int = binaryen.i32; export const bool = binaryen.i32; export const cstring = binaryen.i32; export let ts_string = stringTypeInfo.typeRef; export const pointer = binaryen.i32; export const external_ref_tag = binaryen.i32; // export const const module = new binaryen.Module(); export const bool_true = module.i32.const(1); export const bool_false = module.i32.const(0); export const DYNTYPE_SUCCESS = module.i32.const(0); export const DYNTYPE_EXCEPTION = module.i32.const(1); export const DYNTYPE_TYPEERR = module.i32.const(2); export const enum ExtObjKind { ExtObj = 0, ExtFunc = 1, ExtArray = 2, } // export dyntype functions export const dyntype_get_context = 'dyntype_get_context'; export const dyntype_new_number = 'dyntype_new_number'; export const dyntype_new_boolean = 'dyntype_new_boolean'; export const dyntype_new_string = 'dyntype_new_string'; export const dyntype_new_undefined = 'dyntype_new_undefined'; export const dyntype_new_null = 'dyntype_new_null'; export const dyntype_new_object = 'dyntype_new_object'; export const dyntype_new_array = 'dyntype_new_array'; export const dyntype_add_elem = 'dyntype_add_elem'; export const dyntype_set_elem = 'dyntype_set_elem'; export const dyntype_get_elem = 'dyntype_get_elem'; export const dyntype_new_extref = 'dyntype_new_extref'; export const dyntype_set_property = 'dyntype_set_property'; export const dyntype_define_property = 'dyntype_define_property'; export const dyntype_get_property = 'dyntype_get_property'; export const dyntype_has_property = 'dyntype_has_property'; export const dyntype_delete_property = 'dyntype_delete_property'; export const dyntype_get_keys = 'dyntype_get_keys'; export const dyntype_is_undefined = 'dyntype_is_undefined'; export const dyntype_is_null = 'dyntype_is_null'; export const dyntype_is_bool = 'dyntype_is_bool'; export const dyntype_to_bool = 'dyntype_to_bool'; export const dyntype_is_number = 'dyntype_is_number'; export const dyntype_to_number = 'dyntype_to_number'; export const dyntype_is_string = 'dyntype_is_string'; export const dyntype_to_cstring = 'dyntype_to_cstring'; export const dyntype_to_string = 'dyntype_to_string'; export const dyntype_is_object = 'dyntype_is_object'; export const dyntype_is_array = 'dyntype_is_array'; export const dyntype_is_extref = 'dyntype_is_extref'; export const dyntype_to_extref = 'dyntype_to_extref'; export const dyntype_is_falsy = 'dyntype_is_falsy'; export const dyntype_cmp = 'dyntype_cmp'; export const dyntype_typeof = 'dyntype_typeof'; export const dyntype_typeof1 = 'dyntype_typeof1'; export const dyntype_type_eq = 'dyntype_type_eq'; export const dyntype_toString = 'dyntype_toString'; export const dyntype_new_object_with_proto = 'dyntype_new_object_with_proto'; export const dyntype_new_object_with_class = 'dyntype_new_object_with_class'; export const dyntype_invoke = 'dyntype_invoke'; export const dyntype_get_global = 'dyntype_get_global'; export const dyntype_set_prototype = 'dyntype_set_prototype'; export const dyntype_get_prototype = 'dyntype_get_prototype'; export const dyntype_get_own_property = 'dyntype_get_own_property'; export const dyntype_instanceof = 'dyntype_instanceof'; export const dyntype_dump_value = 'dyntype_dump_value'; export const dyntype_dump_value_buffer = 'dyntype_dump_value_buffer'; export const dyntype_hold = 'dyntype_hold'; export const dyntype_release = 'dyntype_release'; export const dyntype_collect = 'dyntype_collect'; export function updateValueByConfig() { ts_string = getConfig().enableStringRef ? binaryenCAPI._BinaryenTypeStringref() : stringTypeInfo.typeRef; } } export namespace structdyn { export const module_name = 'libstruct_indirect'; export const enum StructDyn { struct_get_indirect_i32 = 'struct_get_indirect_i32', struct_get_indirect_i64 = 'struct_get_indirect_i64', struct_get_indirect_f32 = 'struct_get_indirect_f32', struct_get_indirect_f64 = 'struct_get_indirect_f64', struct_get_indirect_anyref = 'struct_get_indirect_anyref', struct_get_indirect_funcref = 'struct_get_indirect_funcref', struct_set_indirect_i32 = 'struct_set_indirect_i32', struct_set_indirect_i64 = 'struct_set_indirect_i64', struct_set_indirect_f32 = 'struct_set_indirect_f32', struct_set_indirect_f64 = 'struct_set_indirect_f64', struct_set_indirect_anyref = 'struct_set_indirect_anyref', struct_set_indirect_funcref = 'struct_set_indirect_funcref', } } ================================================ FILE: src/backend/binaryen/lib/env_init.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import binaryen from 'binaryen'; import { dyntype, structdyn } from './dyntype/utils.js'; import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; import { UtilFuncs } from '../utils.js'; import { BuiltinNames } from '../../../../lib/builtin/builtin_name.js'; import { getBuiltInFuncName } from '../../../utils.js'; import { i8ArrayTypeInfo } from '../glue/packType.js'; import { _BinaryenTypeStringref } from '../glue/binaryen.js'; export function importAnyLibAPI(module: binaryen.Module) { dyntype.updateValueByConfig(); module.addFunctionImport( dyntype.dyntype_get_context, dyntype.module_name, dyntype.dyntype_get_context, binaryen.createType([]), dyntype.dyn_ctx_t, ); module.addFunctionImport( dyntype.dyntype_new_number, dyntype.module_name, dyntype.dyntype_new_number, binaryen.createType([dyntype.dyn_ctx_t, dyntype.double]), dyntype.dyn_value_t, ); module.addFunctionImport( dyntype.dyntype_new_string, dyntype.module_name, dyntype.dyntype_new_string, binaryen.createType([dyntype.dyn_ctx_t, dyntype.dyn_value_t]), dyntype.dyn_value_t, ); module.addFunctionImport( dyntype.dyntype_new_boolean, dyntype.module_name, dyntype.dyntype_new_boolean, binaryen.createType([dyntype.dyn_ctx_t, dyntype.bool]), dyntype.dyn_value_t, ); module.addFunctionImport( dyntype.dyntype_typeof, dyntype.module_name, dyntype.dyntype_typeof, binaryen.createType([dyntype.dyn_ctx_t, dyntype.dyn_value_t]), dyntype.ts_string, ); module.addFunctionImport( dyntype.dyntype_typeof1, dyntype.module_name, dyntype.dyntype_typeof1, binaryen.createType([dyntype.dyn_ctx_t, dyntype.dyn_value_t]), dyntype.int, ); module.addFunctionImport( dyntype.dyntype_toString, dyntype.module_name, dyntype.dyntype_toString, binaryen.createType([dyntype.dyn_ctx_t, dyntype.dyn_value_t]), dyntype.ts_string, ); module.addFunctionImport( dyntype.dyntype_type_eq, dyntype.module_name, dyntype.dyntype_type_eq, binaryen.createType([ dyntype.dyn_ctx_t, dyntype.dyn_value_t, dyntype.dyn_value_t, ]), dyntype.bool, ); module.addFunctionImport( dyntype.dyntype_is_number, dyntype.module_name, dyntype.dyntype_is_number, binaryen.createType([dyntype.dyn_ctx_t, dyntype.dyn_value_t]), dyntype.bool, ); module.addFunctionImport( dyntype.dyntype_to_number, dyntype.module_name, dyntype.dyntype_to_number, binaryen.createType([dyntype.dyn_ctx_t, dyntype.dyn_value_t]), dyntype.double, ); module.addFunctionImport( dyntype.dyntype_is_undefined, dyntype.module_name, dyntype.dyntype_is_undefined, binaryen.createType([dyntype.dyn_ctx_t, dyntype.dyn_value_t]), dyntype.bool, ); module.addFunctionImport( dyntype.dyntype_is_null, dyntype.module_name, dyntype.dyntype_is_null, binaryen.createType([dyntype.dyn_ctx_t, dyntype.dyn_value_t]), dyntype.bool, ); module.addFunctionImport( dyntype.dyntype_new_undefined, dyntype.module_name, dyntype.dyntype_new_undefined, dyntype.dyn_ctx_t, dyntype.dyn_value_t, ); module.addFunctionImport( dyntype.dyntype_new_null, dyntype.module_name, dyntype.dyntype_new_null, dyntype.dyn_ctx_t, dyntype.dyn_value_t, ); module.addFunctionImport( dyntype.dyntype_new_object, dyntype.module_name, dyntype.dyntype_new_object, dyntype.dyn_ctx_t, dyntype.dyn_value_t, ); module.addFunctionImport( dyntype.dyntype_new_array, dyntype.module_name, dyntype.dyntype_new_array, binaryen.createType([dyntype.dyn_ctx_t, dyntype.int]), dyntype.dyn_value_t, ); module.addFunctionImport( dyntype.dyntype_add_elem, dyntype.module_name, dyntype.dyntype_add_elem, binaryen.createType([ dyntype.dyn_ctx_t, dyntype.dyn_value_t, dyntype.dyn_value_t, ]), dyntype.cvoid, ); module.addFunctionImport( dyntype.dyntype_set_elem, dyntype.module_name, dyntype.dyntype_set_elem, binaryen.createType([ dyntype.dyn_ctx_t, dyntype.dyn_value_t, dyntype.int, dyntype.dyn_value_t, ]), dyntype.cvoid, ); module.addFunctionImport( dyntype.dyntype_get_elem, dyntype.module_name, dyntype.dyntype_get_elem, binaryen.createType([ dyntype.dyn_ctx_t, dyntype.dyn_value_t, dyntype.int, ]), dyntype.dyn_value_t, ); module.addFunctionImport( dyntype.dyntype_is_array, dyntype.module_name, dyntype.dyntype_is_array, binaryen.createType([dyntype.dyn_ctx_t, dyntype.dyn_value_t]), dyntype.bool, ); module.addFunctionImport( dyntype.dyntype_set_property, dyntype.module_name, dyntype.dyntype_set_property, binaryen.createType([ dyntype.dyn_ctx_t, dyntype.dyn_value_t, dyntype.cstring, dyntype.dyn_value_t, ]), dyntype.bool, ); module.addFunctionImport( dyntype.dyntype_get_property, dyntype.module_name, dyntype.dyntype_get_property, binaryen.createType([ dyntype.dyn_ctx_t, dyntype.dyn_value_t, dyntype.cstring, ]), dyntype.dyn_value_t, ); module.addFunctionImport( dyntype.dyntype_has_property, dyntype.module_name, dyntype.dyntype_has_property, binaryen.createType([ dyntype.dyn_ctx_t, dyntype.dyn_value_t, dyntype.cstring, ]), dyntype.int, ); module.addFunctionImport( dyntype.dyntype_delete_property, dyntype.module_name, dyntype.dyntype_delete_property, binaryen.createType([ dyntype.dyn_ctx_t, dyntype.dyn_value_t, dyntype.cstring, ]), dyntype.int, ); module.addFunctionImport( dyntype.dyntype_get_keys, dyntype.module_name, dyntype.dyntype_get_keys, binaryen.createType([dyntype.dyn_ctx_t, dyntype.dyn_value_t]), dyntype.dyn_value_t, ); module.addFunctionImport( dyntype.dyntype_new_extref, dyntype.module_name, dyntype.dyntype_new_extref, binaryen.createType([ dyntype.dyn_ctx_t, dyntype.pointer, dyntype.external_ref_tag, ]), dyntype.dyn_value_t, ); module.addFunctionImport( dyntype.dyntype_is_extref, dyntype.module_name, dyntype.dyntype_is_extref, binaryen.createType([dyntype.dyn_ctx_t, dyntype.dyn_value_t]), dyntype.bool, ); module.addFunctionImport( dyntype.dyntype_to_extref, dyntype.module_name, dyntype.dyntype_to_extref, binaryen.createType([dyntype.dyn_ctx_t, dyntype.dyn_value_t]), dyntype.int, ); module.addFunctionImport( dyntype.dyntype_instanceof, dyntype.module_name, dyntype.dyntype_instanceof, binaryen.createType([ dyntype.dyn_ctx_t, dyntype.dyn_value_t, dyntype.dyn_value_t, ]), dyntype.int, ); module.addFunctionImport( dyntype.dyntype_is_object, dyntype.module_name, dyntype.dyntype_is_object, binaryen.createType([dyntype.dyn_ctx_t, dyntype.dyn_value_t]), dyntype.bool, ); module.addFunctionImport( dyntype.dyntype_get_prototype, dyntype.module_name, dyntype.dyntype_get_prototype, binaryen.createType([dyntype.dyn_ctx_t, dyntype.dyn_value_t]), dyntype.dyn_value_t, ); module.addFunctionImport( dyntype.dyntype_set_prototype, dyntype.module_name, dyntype.dyntype_set_prototype, binaryen.createType([ dyntype.dyn_ctx_t, dyntype.dyn_value_t, dyntype.dyn_value_t, ]), dyntype.int, ); module.addFunctionImport( dyntype.dyntype_is_bool, dyntype.module_name, dyntype.dyntype_is_bool, binaryen.createType([dyntype.dyn_ctx_t, dyntype.dyn_value_t]), dyntype.bool, ); module.addFunctionImport( dyntype.dyntype_to_bool, dyntype.module_name, dyntype.dyntype_to_bool, binaryen.createType([dyntype.dyn_ctx_t, dyntype.dyn_value_t]), dyntype.bool, ); module.addFunctionImport( dyntype.dyntype_is_string, dyntype.module_name, dyntype.dyntype_is_string, binaryen.createType([dyntype.dyn_ctx_t, dyntype.dyn_value_t]), dyntype.bool, ); module.addFunctionImport( dyntype.dyntype_to_string, dyntype.module_name, dyntype.dyntype_to_string, binaryen.createType([dyntype.dyn_ctx_t, dyntype.dyn_value_t]), dyntype.ts_string, ); module.addFunctionImport( dyntype.dyntype_is_falsy, dyntype.module_name, dyntype.dyntype_is_falsy, binaryen.createType([dyntype.dyn_ctx_t, dyntype.dyn_value_t]), dyntype.bool, ); module.addFunctionImport( dyntype.dyntype_cmp, dyntype.module_name, dyntype.dyntype_cmp, binaryen.createType([ dyntype.dyn_ctx_t, dyntype.dyn_value_t, dyntype.dyn_value_t, dyntype.int, ]), dyntype.bool, ); module.addFunctionImport( dyntype.dyntype_new_object_with_class, dyntype.module_name, dyntype.dyntype_new_object_with_class, binaryen.createType([ dyntype.dyn_ctx_t, dyntype.pointer, dyntype.dyn_value_t, ]), dyntype.dyn_value_t, ); module.addFunctionImport( dyntype.dyntype_invoke, dyntype.module_name, dyntype.dyntype_invoke, binaryen.createType([ dyntype.dyn_ctx_t, dyntype.pointer, dyntype.dyn_value_t, dyntype.dyn_value_t, ]), dyntype.dyn_value_t, ); module.addFunctionImport( dyntype.dyntype_get_global, dyntype.module_name, dyntype.dyntype_get_global, binaryen.createType([dyntype.dyn_ctx_t, dyntype.pointer]), dyntype.dyn_value_t, ); } export function importInfcLibAPI(module: binaryen.Module) { module.addFunctionImport( structdyn.StructDyn.struct_get_indirect_i32, structdyn.module_name, structdyn.StructDyn.struct_get_indirect_i32, binaryen.createType([binaryen.anyref, binaryen.i32]), binaryen.i32, ); module.addFunctionImport( structdyn.StructDyn.struct_get_indirect_i64, structdyn.module_name, structdyn.StructDyn.struct_get_indirect_i64, binaryen.createType([binaryen.anyref, binaryen.i32]), binaryen.i64, ); module.addFunctionImport( structdyn.StructDyn.struct_get_indirect_f32, structdyn.module_name, structdyn.StructDyn.struct_get_indirect_f32, binaryen.createType([binaryen.anyref, binaryen.i32]), binaryen.f32, ); module.addFunctionImport( structdyn.StructDyn.struct_get_indirect_f64, structdyn.module_name, structdyn.StructDyn.struct_get_indirect_f64, binaryen.createType([binaryen.anyref, binaryen.i32]), binaryen.f64, ); module.addFunctionImport( structdyn.StructDyn.struct_get_indirect_anyref, structdyn.module_name, structdyn.StructDyn.struct_get_indirect_anyref, binaryen.createType([binaryen.anyref, binaryen.i32]), binaryen.anyref, ); module.addFunctionImport( structdyn.StructDyn.struct_get_indirect_funcref, structdyn.module_name, structdyn.StructDyn.struct_get_indirect_funcref, binaryen.createType([binaryen.anyref, binaryen.i32]), binaryen.funcref, ); module.addFunctionImport( structdyn.StructDyn.struct_set_indirect_i32, structdyn.module_name, structdyn.StructDyn.struct_set_indirect_i32, binaryen.createType([binaryen.anyref, binaryen.i32, binaryen.i32]), binaryen.none, ); module.addFunctionImport( structdyn.StructDyn.struct_set_indirect_i64, structdyn.module_name, structdyn.StructDyn.struct_set_indirect_i64, binaryen.createType([binaryen.anyref, binaryen.i32, binaryen.i64]), binaryen.none, ); module.addFunctionImport( structdyn.StructDyn.struct_set_indirect_f32, structdyn.module_name, structdyn.StructDyn.struct_set_indirect_f32, binaryen.createType([binaryen.anyref, binaryen.i32, binaryen.f32]), binaryen.none, ); module.addFunctionImport( structdyn.StructDyn.struct_set_indirect_f64, structdyn.module_name, structdyn.StructDyn.struct_set_indirect_f64, binaryen.createType([binaryen.anyref, binaryen.i32, binaryen.f64]), binaryen.none, ); module.addFunctionImport( structdyn.StructDyn.struct_set_indirect_anyref, structdyn.module_name, structdyn.StructDyn.struct_set_indirect_anyref, binaryen.createType([binaryen.anyref, binaryen.i32, binaryen.anyref]), binaryen.none, ); module.addFunctionImport( structdyn.StructDyn.struct_set_indirect_funcref, structdyn.module_name, structdyn.StructDyn.struct_set_indirect_funcref, binaryen.createType([binaryen.anyref, binaryen.i32, binaryen.funcref]), binaryen.none, ); } export function importMemoryAPI(module: binaryen.Module) { module.addFunctionImport( BuiltinNames.mallocFunc, BuiltinNames.externalModuleName, BuiltinNames.mallocFunc, binaryen.createType([binaryen.i32]), binaryen.i32, ); module.addFunctionImport( BuiltinNames.freeFunc, BuiltinNames.externalModuleName, BuiltinNames.freeFunc, binaryen.createType([binaryen.i32]), binaryen.none, ); } export function generateGlobalContext(module: binaryen.Module) { module.addGlobal( dyntype.dyntype_context, dyntype.dyn_ctx_t, true, module.ref.null(dyntype.dyn_ctx_t), ); } export function generateGlobalJSObject(module: binaryen.Module, name: string) { module.addGlobal( name, dyntype.dyn_value_t, true, module.ref.null(dyntype.dyn_value_t), ); } export function generateExtRefTableMaskArr(module: binaryen.Module) { const name = getBuiltInFuncName(BuiltinNames.extRefTableMaskArr); module.addGlobal( name, i8ArrayTypeInfo.typeRef, true, module.ref.null(dyntype.dyn_ctx_t), ); } export function generateDynContext(module: binaryen.Module) { const initDynContextStmt = module.global.set( dyntype.dyntype_context, module.call(dyntype.dyntype_get_context, [], binaryen.none), ); return initDynContextStmt; } export function addItableFunc(module: binaryen.Module) { /* add customize function from .wat * /* TODO: Have not found an effiective way to load import function from .wat yet */ module.addFunctionImport( 'strcmp', 'env', 'strcmp', binaryen.createType([binaryen.i32, binaryen.i32]), binaryen.i32, ); const itableFilePath = path.join( path.dirname(fileURLToPath(import.meta.url)), 'interface', 'meta.wat', ); const itableLib = fs.readFileSync(itableFilePath, 'utf-8'); const watModule = binaryen.parseText(itableLib); UtilFuncs.addWatFuncs( watModule, BuiltinNames.findPropertyFlagAndIndex, module, ); module.addFunctionExport( BuiltinNames.findPropertyFlagAndIndex, BuiltinNames.findPropertyFlagAndIndex, ); UtilFuncs.addWatFuncs(watModule, BuiltinNames.findPropertyType, module); watModule.dispose(); } ================================================ FILE: src/backend/binaryen/lib/init_builtin_api.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import binaryen from 'binaryen'; import * as binaryenCAPI from '../glue/binaryen.js'; import { BuiltinNames } from '../../../../lib/builtin/builtin_name.js'; import { StringRefAsOp, StringRefEqOp, StringRefMeatureOp, StringRefSliceOp, StringRefNewOp, baseStructType, emptyStructType, arrayToPtr, } from '../glue/transform.js'; import { UtilFuncs, FunctionalFuncs, FlattenLoop, MetaDataOffset, META_FLAG_MASK, ItableFlag, MetaPropertyOffset, SIZE_OF_META_FIELD, } from '../utils.js'; import { dyntype, structdyn } from './dyntype/utils.js'; import { i8ArrayTypeInfo, stringArrayTypeInfo, stringArrayStructTypeInfo, stringTypeInfo, stringrefArrayStructTypeInfo, stringrefArrayTypeInfo, arrayBufferTypeInfo, dataViewTypeInfo, numberArrayStructTypeInfo, numberArrayTypeInfo, i32ArrayTypeInfo, } from '../glue/packType.js'; import { array_get_data, array_get_length_i32 } from './array_utils.js'; import { SemanticsKind } from '../../../semantics/semantics_nodes.js'; import { ValueTypeKind } from '../../../semantics/value_types.js'; import { PredefinedTypeId, getBuiltInFuncName, getUtilsFuncName, } from '../../../utils.js'; import { getConfig } from '../../../../config/config_mgr.js'; import { memoryAlignment } from '../memory.js'; function anyrefCond(module: binaryen.Module) { const ref = module.local.get(0, binaryen.anyref); const dynCtx = binaryenCAPI._BinaryenGlobalGet( module.ptr, UtilFuncs.getCString(dyntype.dyntype_context), dyntype.dyn_ctx_t, ); const cond = module.call( dyntype.dyntype_is_extref, [dynCtx, ref], dyntype.bool, ); const index = module.call( dyntype.dyntype_to_extref, [dynCtx, ref], dyntype.int, ); const extRef = binaryenCAPI._BinaryenTableGet( module.ptr, UtilFuncs.getCString(BuiltinNames.extrefTable), index, binaryen.anyref, ); const ifTrue = module.block(null, [ module.return( module.i32.eqz(binaryenCAPI._BinaryenRefIsNull(module.ptr, extRef)), ), ]); const falsy = module.call( dyntype.dyntype_is_falsy, [dynCtx, ref], dyntype.bool, ); const ifFalse = module.if( falsy, module.return(module.i32.const(0)), module.return(module.i32.const(1)), ); const stmt = module.if(cond, ifTrue, ifFalse); return module.block(null, [stmt], binaryen.i32); } function getPropNameThroughMeta(module: binaryen.Module) { const objIndex = 0, elemsIndex = 1, metaIndex = 2, metaFieldsCountIndex = 3, metaFieldsPtrIndex = 4, propNameIndex = 5, fieldFlagIndex = 6, loopIndex = 7, iterPropCountIndex = 8, curCharIndex = 9, strLenIndex = 10; const obj = module.local.get(objIndex, baseStructType.typeRef); const elems = module.local.get(elemsIndex, stringrefArrayTypeInfo.typeRef); const meta = module.local.get(metaIndex, binaryen.i32); const mataFieldsCount = module.local.get( metaFieldsCountIndex, binaryen.i32, ); const metaFieldsPtr = module.local.get(metaFieldsPtrIndex, binaryen.i32); const propName = module.local.get(propNameIndex, binaryen.i32); const fieldFlag = module.local.get(fieldFlagIndex, binaryen.i32); const loopIdx = module.local.get(loopIndex, binaryen.i32); const iterPropCount = module.local.get(iterPropCountIndex, binaryen.i32); const curChar = module.local.get(curCharIndex, binaryen.i32); const strLen = module.local.get(strLenIndex, binaryen.i32); const statementArray: binaryen.ExpressionRef[] = []; // 1. get meta const metaValue = FunctionalFuncs.getWASMObjectMeta(module, obj); statementArray.push(module.local.set(metaIndex, metaValue)); // 2. get meta fields count statementArray.push( module.local.set( metaFieldsCountIndex, FunctionalFuncs.getFieldFromMetaByOffset( module, meta, MetaDataOffset.COUNT_OFFSET, ), ), ); // 3. get meta fields ptr statementArray.push( module.local.set( metaFieldsPtrIndex, module.i32.add( meta, module.i32.const(MetaDataOffset.FIELDS_PTR_OFFSET), ), ), ); statementArray.push( module.local.set(iterPropCountIndex, module.i32.const(0)), ); // 4. get number of iterable properties, and fill string array by iterable names const loop = (loopLabel: string, ifTrueBlock: binaryen.ExpressionRef) => { const loopInit = module.local.set(loopIndex, module.i32.const(0)); const loopCond = module.i32.lt_u(loopIdx, mataFieldsCount); const loopIncrementor = module.local.set( loopIndex, module.i32.add(loopIdx, module.i32.const(1)), ); const loopStmtsArray: binaryen.ExpressionRef[] = []; const flagAndIndex = module.i32.load( MetaPropertyOffset.FLAG_AND_INDEX_OFFSET, memoryAlignment, metaFieldsPtr, ); loopStmtsArray.push( module.local.set( fieldFlagIndex, module.i32.and(flagAndIndex, module.i32.const(META_FLAG_MASK)), ), ); loopStmtsArray.push( module.if( module.i32.eq(fieldFlag, module.i32.const(ItableFlag.FIELD)), ifTrueBlock, ), ); loopStmtsArray.push( module.local.set( metaFieldsPtrIndex, module.i32.add( metaFieldsPtr, module.i32.const(SIZE_OF_META_FIELD), ), ), ); const forLoop: FlattenLoop = { label: loopLabel, condition: loopCond, statements: module.block(null, loopStmtsArray), incrementor: loopIncrementor, }; statementArray.push(loopInit); statementArray.push( module.loop( loopLabel, FunctionalFuncs.flattenLoopStatement( module, forLoop, SemanticsKind.FOR, ), ), ); }; // get number of iterable properties loop( 'for_label1', module.local.set( iterPropCountIndex, module.i32.add(iterPropCount, module.i32.const(1)), ), ); statementArray.push( module.local.set( elemsIndex, binaryenCAPI._BinaryenArrayNew( module.ptr, stringrefArrayTypeInfo.heapTypeRef, iterPropCount, module.ref.null(binaryen.stringref), ), ), ); // fill string array by iterable names statementArray.push( module.local.set( metaFieldsPtrIndex, module.i32.add( meta, module.i32.const(MetaDataOffset.FIELDS_PTR_OFFSET), ), ), module.local.set(iterPropCountIndex, module.i32.const(0)), ); const getIterPropBlock = module.block(null, [ module.local.set( propNameIndex, module.i32.load( MetaPropertyOffset.NAME_OFFSET, memoryAlignment, metaFieldsPtr, ), ), // get property name length by a loop module.local.set( curCharIndex, module.i32.add(propName, module.i32.const(-1)), ), module.loop( 'label_0', module.block(null, [ module.br_if( 'label_0', module.i32.load8_u( 0, 0, module.local.tee( curCharIndex, module.i32.add(curChar, module.i32.const(1)), binaryen.i32, ), ), ), ]), ), module.local.set(strLenIndex, module.i32.sub(curChar, propName)), binaryenCAPI._BinaryenArraySet( module.ptr, elems, iterPropCount, binaryenCAPI._BinaryenStringNew( module.ptr, StringRefNewOp.UTF8, propName, strLen, 0, 0, false, ), ), module.local.set( iterPropCountIndex, module.i32.add(iterPropCount, module.i32.const(1)), ), ]); loop('for_label2', getIterPropBlock); // 5. create string array const stringArrayRef = binaryenCAPI._BinaryenStructNew( module.ptr, arrayToPtr([elems, iterPropCount]).ptr, 2, stringrefArrayStructTypeInfo.heapTypeRef, ); statementArray.push(module.return(stringArrayRef)); return module.block(null, statementArray); } function string_concat(module: binaryen.Module) { /** Args: context, this, string[] */ const thisStrStructIdx = 1; const paramStrArrayIdx = 2; /** Locals: totalLen, for_i(i32), newStrArrayIdx(char_array), copyCurLenIdx(i32) */ const totalLenIdx = 3; const for_i_Idx = 4; const newStrArrayIdx = 5; const copyCurLenIdx = 6; /** structure index information */ const arrayIdxInStruct = 1; const thisStrStruct = module.local.get( thisStrStructIdx, stringTypeInfo.typeRef, ); const paramStrArray = module.local.get( paramStrArrayIdx, stringArrayStructTypeInfo.typeRef, ); const thisStrArray = binaryenCAPI._BinaryenStructGet( module.ptr, arrayIdxInStruct, thisStrStruct, i8ArrayTypeInfo.typeRef, false, ); const thisStrLen = binaryenCAPI._BinaryenArrayLen(module.ptr, thisStrArray); const paramStrArrayLen = array_get_length_i32(module, paramStrArray); const getStringArrayFromRestParams = (module: binaryen.Module) => { return binaryenCAPI._BinaryenStructGet( module.ptr, arrayIdxInStruct, binaryenCAPI._BinaryenArrayGet( module.ptr, array_get_data(module, paramStrArray), module.local.get(for_i_Idx, binaryen.i32), stringTypeInfo.typeRef, false, ), i8ArrayTypeInfo.typeRef, false, ); }; const statementArray: binaryen.ExpressionRef[] = []; /** 1. get total str length */ statementArray.push(module.local.set(totalLenIdx, thisStrLen)); const for_label_1 = 'for_loop_1_block'; const for_init_1 = module.local.set(for_i_Idx, module.i32.const(0)); const for_condition_1 = module.i32.lt_u( module.local.get(for_i_Idx, binaryen.i32), paramStrArrayLen, ); const for_incrementor_1 = module.local.set( for_i_Idx, module.i32.add( module.local.get(for_i_Idx, binaryen.i32), module.i32.const(1), ), ); const for_body_1 = module.local.set( totalLenIdx, module.i32.add( module.local.get(totalLenIdx, binaryen.i32), binaryenCAPI._BinaryenArrayLen( module.ptr, getStringArrayFromRestParams(module), ), ), ); const flattenLoop_1: FlattenLoop = { label: for_label_1, condition: for_condition_1, statements: for_body_1, incrementor: for_incrementor_1, }; statementArray.push(for_init_1); statementArray.push( module.loop( for_label_1, FunctionalFuncs.flattenLoopStatement( module, flattenLoop_1, SemanticsKind.FOR, ), ), ); /** 2. generate new string */ statementArray.push( module.local.set( newStrArrayIdx, binaryenCAPI._BinaryenArrayNew( module.ptr, i8ArrayTypeInfo.heapTypeRef, module.local.get(totalLenIdx, binaryen.i32), module.i32.const(0), ), ), ); /** 3. traverse paramStrArray, do copy */ statementArray.push( binaryenCAPI._BinaryenArrayCopy( module.ptr, module.local.get(newStrArrayIdx, i8ArrayTypeInfo.typeRef), module.i32.const(0), thisStrArray, module.i32.const(0), thisStrLen, ), ); statementArray.push(module.local.set(copyCurLenIdx, thisStrLen)); const for_label_2 = 'for_loop_2_block'; const for_init_2 = module.local.set(for_i_Idx, module.i32.const(0)); const for_condition_2 = module.i32.lt_u( module.local.get(for_i_Idx, binaryen.i32), paramStrArrayLen, ); const for_incrementor_2 = module.local.set( for_i_Idx, module.i32.add( module.local.get(for_i_Idx, binaryen.i32), module.i32.const(1), ), ); const for_body_2 = module.block(null, [ binaryenCAPI._BinaryenArrayCopy( module.ptr, module.local.get(newStrArrayIdx, i8ArrayTypeInfo.typeRef), module.local.get(copyCurLenIdx, binaryen.i32), getStringArrayFromRestParams(module), module.i32.const(0), binaryenCAPI._BinaryenArrayLen( module.ptr, getStringArrayFromRestParams(module), ), ), module.local.set( copyCurLenIdx, module.i32.add( module.local.get(copyCurLenIdx, binaryen.i32), binaryenCAPI._BinaryenArrayLen( module.ptr, getStringArrayFromRestParams(module), ), ), ), ]); const flattenLoop_2: FlattenLoop = { label: for_label_2, condition: for_condition_2, statements: for_body_2, incrementor: for_incrementor_2, }; statementArray.push(for_init_2); statementArray.push( module.loop( for_label_2, FunctionalFuncs.flattenLoopStatement( module, flattenLoop_2, SemanticsKind.FOR, ), ), ); /** 4. generate new string structure */ statementArray.push( module.return( binaryenCAPI._BinaryenStructNew( module.ptr, arrayToPtr([ module.i32.const(0), module.local.get(newStrArrayIdx, i8ArrayTypeInfo.typeRef), ]).ptr, 2, stringTypeInfo.heapTypeRef, ), ), ); /** 5. generate block, return block */ const concatBlock = module.block('concat', statementArray); return concatBlock; } function string_concat_stringref(module: binaryen.Module) { const type = binaryenCAPI._BinaryenTypeStringref(); const refIndex = 1; const strsIndex = 2; const strs = module.local.get( strsIndex, stringrefArrayStructTypeInfo.typeRef, ); const resIndex = 3; const res = module.local.get(resIndex, type); const strsLenIndex = 4; const strsLen = module.local.get(strsLenIndex, binaryen.i32); const loopIndexIndex = 5; const loopIndex = module.local.get(loopIndexIndex, binaryen.i32); const strArrayIndex = 6; const strArray = module.local.get( strArrayIndex, stringrefArrayTypeInfo.typeRef, ); const statementArray: binaryen.ExpressionRef[] = []; statementArray.push( module.local.set(resIndex, module.local.get(refIndex, type)), ); const len = binaryenCAPI._BinaryenStructGet( module.ptr, 1, strs, binaryen.i32, false, ); const strsContent = binaryenCAPI._BinaryenStructGet( module.ptr, 0, strs, stringrefArrayStructTypeInfo.typeRef, false, ); statementArray.push(module.local.set(strsLenIndex, len)); statementArray.push(module.local.set(loopIndexIndex, module.i32.const(0))); statementArray.push(module.local.set(strArrayIndex, strsContent)); const loopLabel = 'for_label'; const loopCond = module.i32.lt_s(loopIndex, strsLen); const loopIncrementor = module.local.set( loopIndexIndex, module.i32.add(loopIndex, module.i32.const(1)), ); const loopBody: binaryen.ExpressionRef[] = []; const concat = module.local.set( resIndex, binaryenCAPI._BinaryenStringConcat( module.ptr, res, binaryenCAPI._BinaryenArrayGet( module.ptr, strArray, loopIndex, type, false, ), ), ); loopBody.push(concat); const flattenLoop: FlattenLoop = { label: loopLabel, condition: loopCond, statements: module.block(null, loopBody), incrementor: loopIncrementor, }; statementArray.push( module.loop( loopLabel, FunctionalFuncs.flattenLoopStatement( module, flattenLoop, SemanticsKind.FOR, ), ), ); statementArray.push(module.return(res)); return module.block('concat', statementArray); } function string_eq(module: binaryen.Module) { const statementArray: binaryen.ExpressionRef[] = []; const leftstrIdx = 0; const rightstrIdx = 1; const for_i_Idx = 2; const leftstr = module.local.get(leftstrIdx, stringTypeInfo.typeRef); const rightstr = module.local.get(rightstrIdx, stringTypeInfo.typeRef); const leftstrArray = binaryenCAPI._BinaryenStructGet( module.ptr, 1, leftstr, i8ArrayTypeInfo.typeRef, false, ); const leftstrLen = binaryenCAPI._BinaryenArrayLen(module.ptr, leftstrArray); const rightstrArray = binaryenCAPI._BinaryenStructGet( module.ptr, 1, rightstr, i8ArrayTypeInfo.typeRef, false, ); const rightstrLen = binaryenCAPI._BinaryenArrayLen( module.ptr, rightstrArray, ); const retfalseLenNoEq = module.if( module.i32.ne(leftstrLen, rightstrLen), module.return(module.i32.const(0)), ); statementArray.push(retfalseLenNoEq); const for_label_1 = 'for_loop_1_block'; const for_init_1 = module.local.set(for_i_Idx, module.i32.const(0)); const for_condition_1 = module.i32.lt_u( module.local.get(for_i_Idx, binaryen.i32), leftstrLen, ); const for_incrementor_1 = module.local.set( for_i_Idx, module.i32.add( module.local.get(for_i_Idx, binaryen.i32), module.i32.const(1), ), ); const for_body_1 = module.if( module.i32.ne( binaryenCAPI._BinaryenArrayGet( module.ptr, leftstrArray, module.local.get(for_i_Idx, binaryen.i32), i8ArrayTypeInfo.typeRef, false, ), binaryenCAPI._BinaryenArrayGet( module.ptr, rightstrArray, module.local.get(for_i_Idx, binaryen.i32), i8ArrayTypeInfo.typeRef, false, ), ), module.return(module.i32.const(0)), ); const flattenLoop_1: FlattenLoop = { label: for_label_1, condition: for_condition_1, statements: for_body_1, incrementor: for_incrementor_1, }; statementArray.push(for_init_1); statementArray.push( module.loop( for_label_1, FunctionalFuncs.flattenLoopStatement( module, flattenLoop_1, SemanticsKind.FOR, ), ), ); statementArray.push(module.return(module.i32.const(1))); const stringeqBlock = module.block(null, statementArray); return stringeqBlock; } function string_eq_stringref(module: binaryen.Module) { const left_str_index = 0; const right_str_index = 1; const left_str = module.local.get( left_str_index, binaryenCAPI._BinaryenTypeStringref(), ); const right_str = module.local.get( right_str_index, binaryenCAPI._BinaryenTypeStringref(), ); return binaryenCAPI._BinaryenStringEq( module.ptr, StringRefEqOp.EQ, left_str, right_str, ); } function string_slice(module: binaryen.Module) { /** Args: context, this, start, end */ const thisStrStructIdx = 1; const startParamIdx = 2; const endParamIdx = 3; /** Locals: start_i32, end_i32 */ const startI32Idx = 4; const endI32Idx = 5; const newStrArrayIndex = 6; /** structure index information */ const arrayIdxInStruct = 1; /** invoke binaryen API */ const thisStrStruct = module.local.get( thisStrStructIdx, stringTypeInfo.typeRef, ); const startAnyRef = module.local.get(startParamIdx, binaryen.anyref); const endAnyRef = module.local.get(endParamIdx, binaryen.anyref); const statementArray: binaryen.ExpressionRef[] = []; const strArray = binaryenCAPI._BinaryenStructGet( module.ptr, arrayIdxInStruct, thisStrStruct, i8ArrayTypeInfo.typeRef, false, ); const strLen = binaryenCAPI._BinaryenArrayLen(module.ptr, strArray); /** 1. set start and end to i32 */ const setAnyToI32 = ( module: binaryen.Module, localIdx: number, anyRef: binaryen.ExpressionRef, defaultValue: binaryen.ExpressionRef, ) => { const isUndefined = FunctionalFuncs.isBaseType( module, anyRef, dyntype.dyntype_is_undefined, ); const dynToNumberValue = FunctionalFuncs.unboxAnyToBase( module, anyRef, ValueTypeKind.NUMBER, ); // get passed param value by string length const paramValue = module.if( module.f64.lt(dynToNumberValue, module.f64.const(0)), module.if( module.i32.le_s( module.i32.add( module.i32.trunc_s_sat.f64(dynToNumberValue), strLen, ), module.i32.const(0), ), module.i32.const(0), module.i32.add( module.i32.trunc_s_sat.f64(dynToNumberValue), strLen, ), ), module.if( module.i32.le_s( module.i32.trunc_s_sat.f64(dynToNumberValue), strLen, ), module.i32.trunc_s_sat.f64(dynToNumberValue), strLen, ), ); return module.if( module.i32.ne(isUndefined, module.i32.const(0)), module.local.set(localIdx, defaultValue), module.local.set(localIdx, paramValue), ); }; const setStartAnyToI32Ref = setAnyToI32( module, startI32Idx, startAnyRef, module.i32.const(0), ); const setEndAnyToI32Ref = setAnyToI32(module, endI32Idx, endAnyRef, strLen); statementArray.push(setStartAnyToI32Ref); statementArray.push(setEndAnyToI32Ref); /** 2. get new string length */ const start = module.local.get(startI32Idx, binaryen.i32); const end = module.local.get(endI32Idx, binaryen.i32); const newStrLen = module.if( module.i32.le_s(start, end), module.i32.sub(end, start), module.i32.const(0), ); /** 3. copy value to new string */ const newStrArrayType = i8ArrayTypeInfo.typeRef; const newStrArrayStatement = module.local.set( newStrArrayIndex, binaryenCAPI._BinaryenArrayNew( module.ptr, i8ArrayTypeInfo.heapTypeRef, newStrLen, module.i32.const(0), ), ); statementArray.push(newStrArrayStatement); const arrayCopyStatement = module.if( module.i32.ne(newStrLen, module.i32.const(0)), binaryenCAPI._BinaryenArrayCopy( module.ptr, module.local.get(newStrArrayIndex, newStrArrayType), module.i32.const(0), strArray, start, newStrLen, ), ); statementArray.push(arrayCopyStatement); /** 4. generate new string structure */ const newStrStruct = binaryenCAPI._BinaryenStructNew( module.ptr, arrayToPtr([ module.i32.const(0), module.local.get(newStrArrayIndex, newStrArrayType), ]).ptr, 2, stringTypeInfo.heapTypeRef, ); statementArray.push(module.return(newStrStruct)); /** 5. generate block, return block */ const sliceBlock = module.block('slice', statementArray); return sliceBlock; } function string_slice_stringref(module: binaryen.Module) { const ref_index = 1; const start_index = 2; const end_index = 3; const start_i32_index = 4; const end_i32_index = 5; const value_index = 6; const value = module.local.get(value_index, binaryen.i32); const view_wtf16_index = 7; const ref = module.local.get( ref_index, binaryenCAPI._BinaryenTypeStringref(), ); const start = module.local.get(start_i32_index, binaryen.i32); const end = module.local.get(end_i32_index, binaryen.i32); const view_wtf16 = module.local.get( view_wtf16_index, binaryenCAPI._BinaryenTypeStringviewWTF16(), ); const len = binaryenCAPI._BinaryenStringMeasure( module.ptr, StringRefMeatureOp.WTF16, ref, ); const statementArray: binaryen.ExpressionRef[] = []; statementArray.push( module.local.set( view_wtf16_index, binaryenCAPI._BinaryenStringAs( module.ptr, StringRefAsOp.WTF16, ref, ), ), ); const unboxSliceIndex = ( index: number, defaultVar: binaryen.ExpressionRef, ) => { const anyRef = module.local.get(index, binaryen.anyref); const isUndefined = FunctionalFuncs.isBaseType( module, anyRef, dyntype.dyntype_is_undefined, ); const num = module.i32.trunc_s_sat.f64( module.call( dyntype.dyntype_to_number, [FunctionalFuncs.getDynContextRef(module), anyRef], binaryen.f64, ), ); return module.if( isUndefined, module.local.set(value_index, defaultVar), module.local.set(value_index, num), ); }; const getIndexOffset = (index: number) => { return module.if( module.i32.lt_s(value, module.i32.const(0)), module.local.set(index, module.i32.add(len, value)), module.local.set(index, value), ); }; statementArray.push(unboxSliceIndex(start_index, module.i32.const(0))); statementArray.push(getIndexOffset(start_i32_index)); statementArray.push(unboxSliceIndex(end_index, len)); statementArray.push(getIndexOffset(end_i32_index)); statementArray.push( module.return( binaryenCAPI._BinaryenStringSliceWTF( module.ptr, StringRefSliceOp.WTF16, view_wtf16, start, end, ), ), ); return module.block('slice', statementArray); } function string_replace(module: binaryen.Module) { /** Args: context, this, pattern, targetStr*/ const thisStrStructIdx = 1; const patternStrIdx = 2; const targetStrIdx = 3; /** Locals: new char array, matched position, len of this str, * len of pattern str, len of target str */ const newCharArrayIdx = 4; const matchedPosIdx = 5; /* structure index informations*/ const arrayIdxInStruct = 1; const statementArray: binaryen.ExpressionRef[] = []; /**1. get length of this str*/ const thisStrStruct = module.local.get( thisStrStructIdx, stringTypeInfo.typeRef, ); const thisStrArray = binaryenCAPI._BinaryenStructGet( module.ptr, arrayIdxInStruct, thisStrStruct, i8ArrayTypeInfo.typeRef, false, ); const thisStrLen = binaryenCAPI._BinaryenArrayLen(module.ptr, thisStrArray); /**2. get pattern str and len*/ const patternStrStruct = module.local.get( patternStrIdx, stringTypeInfo.typeRef, ); const patternStrArray = binaryenCAPI._BinaryenStructGet( module.ptr, arrayIdxInStruct, patternStrStruct, i8ArrayTypeInfo.typeRef, false, ); const patternStrLen = binaryenCAPI._BinaryenArrayLen( module.ptr, patternStrArray, ); /**3. Boundary condition */ // 3.1 return if length doesn't meet requirements statementArray.push( module.if( module.i32.lt_s(thisStrLen, patternStrLen), module.return(thisStrStruct), ), ); // 3.2 return if don't match statementArray.push( module.local.set( matchedPosIdx, module.call( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringIndexOfInternalFuncName, ), [ module.local.get(0, emptyStructType.typeRef), module.local.get(thisStrStructIdx, stringTypeInfo.typeRef), module.local.get(patternStrIdx, stringTypeInfo.typeRef), module.i32.const(0), ], binaryen.i32, ), ), ); statementArray.push( module.if( module.i32.eq( module.local.get(matchedPosIdx, binaryen.i32), module.i32.const(-1), ), module.return(thisStrStruct), ), ); /**4. get target str and len */ const targetStrStruct = module.local.get( targetStrIdx, stringTypeInfo.typeRef, ); const targetStrArray = binaryenCAPI._BinaryenStructGet( module.ptr, arrayIdxInStruct, targetStrStruct, i8ArrayTypeInfo.typeRef, false, ); const targetStrLen = binaryenCAPI._BinaryenArrayLen( module.ptr, targetStrArray, ); /**5. create a new string */ const totalLen = module.i32.sub( module.i32.add(thisStrLen, targetStrLen), patternStrLen, ); statementArray.push( module.local.set( newCharArrayIdx, binaryenCAPI._BinaryenArrayNew( module.ptr, i8ArrayTypeInfo.heapTypeRef, totalLen, module.i32.const(0), ), ), ); statementArray.push( binaryenCAPI._BinaryenArrayCopy( module.ptr, module.local.get(newCharArrayIdx, i8ArrayTypeInfo.typeRef), module.i32.const(0), thisStrArray, module.i32.const(0), module.local.get(matchedPosIdx, binaryen.i32), ), ); statementArray.push( binaryenCAPI._BinaryenArrayCopy( module.ptr, module.local.get(newCharArrayIdx, i8ArrayTypeInfo.typeRef), module.local.get(matchedPosIdx, binaryen.i32), targetStrArray, module.i32.const(0), targetStrLen, ), ); statementArray.push( binaryenCAPI._BinaryenArrayCopy( module.ptr, module.local.get(newCharArrayIdx, i8ArrayTypeInfo.typeRef), module.i32.add( module.local.get(matchedPosIdx, binaryen.i32), targetStrLen, ), thisStrArray, module.i32.add( module.local.get(matchedPosIdx, binaryen.i32), patternStrLen, ), module.i32.sub( thisStrLen, module.i32.add( module.local.get(matchedPosIdx, binaryen.i32), patternStrLen, ), ), ), ); statementArray.push( module.return( binaryenCAPI._BinaryenStructNew( module.ptr, arrayToPtr([ module.i32.const(0), module.local.get(newCharArrayIdx, i8ArrayTypeInfo.typeRef), ]).ptr, 2, stringTypeInfo.heapTypeRef, ), ), ); const replaceBlock = module.block('replace', statementArray); return replaceBlock; } function string_replace_stringref(module: binaryen.Module) { const ref_index = 1; const search_str_index = 2; const replace_str_index = 3; const target_pos_index = 4; const search_str_len_index = 5; const ref = module.local.get( ref_index, binaryenCAPI._BinaryenTypeStringref(), ); const search_str = module.local.get( search_str_index, binaryenCAPI._BinaryenTypeStringref(), ); const replace_str = module.local.get( replace_str_index, binaryenCAPI._BinaryenTypeStringref(), ); const target_pos = module.local.get(target_pos_index, binaryen.i32); const search_str_len = module.local.get(search_str_len_index, binaryen.i32); const statementArray: binaryen.ExpressionRef[] = []; const pos = module.call( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringIndexOfInternalFuncName, ), [ module.local.get(0, emptyStructType.typeRef), ref, search_str, module.i32.const(1), module.i32.const(0), ], binaryen.i32, ); statementArray.push(module.local.set(target_pos_index, pos)); statementArray.push( module.if( module.i32.eq(target_pos, module.i32.const(-1)), module.return(ref), ), ); statementArray.push( module.local.set( search_str_len_index, binaryenCAPI._BinaryenStringMeasure( module.ptr, StringRefMeatureOp.WTF16, search_str, ), ), ); let res = binaryenCAPI._BinaryenStringConcat( module.ptr, binaryenCAPI._BinaryenStringSliceWTF( module.ptr, StringRefSliceOp.WTF16, binaryenCAPI._BinaryenStringAs( module.ptr, StringRefAsOp.WTF16, ref, ), module.i32.const(0), target_pos, ), replace_str, ); res = binaryenCAPI._BinaryenStringConcat( module.ptr, res, binaryenCAPI._BinaryenStringSliceWTF( module.ptr, StringRefSliceOp.WTF16, binaryenCAPI._BinaryenStringAs( module.ptr, StringRefAsOp.WTF16, ref, ), module.i32.add(target_pos, search_str_len), module.i32.const(-1), ), ); statementArray.push(res); return module.block('replace', statementArray); } function string_split(module: binaryen.Module) { /** Args: context, this, string*/ const thisStrStructIdx = 1; const sepStrIdx = 2; /** Locals: */ // beging idx for each search const searchBegIdx = 3; // match idx for each search const matchIndexIdx = 4; // lenght of split result const resArrLenIdx = 5; // length of this str const thisStrLenIdx = 6; // length of sep str const sepStrLenIdx = 7; // split result const resStrArrayIdx = 8; // length of split part in each match const curStrLenIdx = 9; // temp char array for every split part const tempCharArrayIdx = 10; // cur index of the operating element in result array const curStrArrayIndexIdx = 11; const arrayIdxInStruct = 1; const statementArray: binaryen.ExpressionRef[] = []; /**0.1 get length of this string*/ const thisStrStruct = module.local.get( thisStrStructIdx, stringTypeInfo.typeRef, ); const thisStrArray = binaryenCAPI._BinaryenStructGet( module.ptr, arrayIdxInStruct, thisStrStruct, i8ArrayTypeInfo.typeRef, false, ); const thisStrLen = binaryenCAPI._BinaryenArrayLen(module.ptr, thisStrArray); statementArray.push(module.local.set(thisStrLenIdx, thisStrLen)); /** 0.2 get length of sep string*/ const sepStrStruct = module.local.get(sepStrIdx, stringTypeInfo.typeRef); const sepStrArray = binaryenCAPI._BinaryenStructGet( module.ptr, arrayIdxInStruct, sepStrStruct, i8ArrayTypeInfo.typeRef, false, ); const sepStrLen = binaryenCAPI._BinaryenArrayLen(module.ptr, sepStrArray); statementArray.push(module.local.set(sepStrLenIdx, sepStrLen)); /**1. cacl len of split array */ const block_label_1 = 'block_label_1'; const loop_label_1 = 'loop_block_1'; const loop_init_1 = module.local.set(searchBegIdx, module.i32.const(0)); const loop_stmts_1 = module.block(null, [ module.local.set( matchIndexIdx, module.call( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringIndexOfInternalFuncName, ), [ module.local.get(0, emptyStructType.typeRef), module.local.get(thisStrStructIdx, stringTypeInfo.typeRef), module.local.get(sepStrIdx, stringTypeInfo.typeRef), module.local.get(searchBegIdx, binaryen.i32), ], binaryen.i32, ), ), // inc length of res string array module.local.set( resArrLenIdx, module.i32.add( module.local.get(resArrLenIdx, binaryen.i32), module.i32.const(1), ), ), // jmp out the loop module.br( block_label_1, module.i32.eq( module.local.get(matchIndexIdx, binaryen.i32), module.i32.const(-1), ), ), // update search begin module.local.set( searchBegIdx, module.i32.add( module.local.get(matchIndexIdx, binaryen.i32), module.local.get(sepStrLenIdx, binaryen.i32), ), ), // jmp to loop again module.br(loop_label_1), ]); const loop_1 = module.loop(loop_label_1, loop_stmts_1); const stmts_block_1 = module.block(block_label_1, [loop_init_1, loop_1]); statementArray.push(stmts_block_1); /**2. create an string array */ statementArray.push( module.local.set( resStrArrayIdx, binaryenCAPI._BinaryenArrayNew( module.ptr, stringArrayTypeInfo.heapTypeRef, module.local.get(resArrLenIdx, binaryen.i32), module.ref.null(stringTypeInfo.typeRef), ), ), ); /**3. copy split part to the result array */ // helper function: const createNewCharArray = (module: binaryen.Module) => { return binaryenCAPI._BinaryenArrayNew( module.ptr, i8ArrayTypeInfo.heapTypeRef, module.local.get(curStrLenIdx, binaryen.i32), module.i32.const(0), ); }; const block_label_2 = 'block_label_2'; const loop_label_2 = 'loop_block_2'; // init search begin idx and current string idx in res array const loop_init_2 = module.block(null, [ module.local.set(searchBegIdx, module.i32.const(0)), module.local.set(curStrArrayIndexIdx, module.i32.const(0)), ]); const loop_stmts_2 = module.block(null, [ module.local.set( matchIndexIdx, module.call( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringIndexOfInternalFuncName, ), [ module.local.get(0, emptyStructType.typeRef), module.local.get(thisStrStructIdx, stringTypeInfo.typeRef), module.local.get(sepStrIdx, stringTypeInfo.typeRef), module.local.get(searchBegIdx, binaryen.i32), ], binaryen.i32, ), ), // cal and set current sub string length module.if( module.i32.eq( module.local.get(matchIndexIdx, binaryen.i32), module.i32.const(-1), ), module.local.set( curStrLenIdx, module.i32.sub( module.local.get(thisStrLenIdx, binaryen.i32), module.local.get(searchBegIdx, binaryen.i32), ), ), module.local.set( curStrLenIdx, module.i32.sub( module.local.get(matchIndexIdx, binaryen.i32), module.local.get(searchBegIdx, binaryen.i32), ), ), ), // create a char array module.local.set(tempCharArrayIdx, createNewCharArray(module)), // fill the array binaryenCAPI._BinaryenArrayCopy( module.ptr, module.local.get(tempCharArrayIdx, i8ArrayTypeInfo.typeRef), module.i32.const(0), thisStrArray, module.local.get(searchBegIdx, binaryen.i32), module.local.get(curStrLenIdx, binaryen.i32), ), // Creates a string and places it in the res array. binaryenCAPI._BinaryenArraySet( module.ptr, module.local.get(resStrArrayIdx, stringArrayTypeInfo.typeRef), module.local.get(curStrArrayIndexIdx, binaryen.i32), binaryenCAPI._BinaryenStructNew( module.ptr, arrayToPtr([ module.i32.const(0), module.local.get(tempCharArrayIdx, i8ArrayTypeInfo.typeRef), ]).ptr, 2, stringTypeInfo.heapTypeRef, ), ), // inc the idx module.local.set( curStrArrayIndexIdx, module.i32.add( module.local.get(curStrArrayIndexIdx, binaryen.i32), module.i32.const(1), ), ), // jmp out the loop module.br( block_label_2, module.i32.eq( module.local.get(matchIndexIdx, binaryen.i32), module.i32.const(-1), ), ), // jmp to loop module.local.set( searchBegIdx, module.i32.add( module.local.get(matchIndexIdx, binaryen.i32), module.local.get(sepStrLenIdx, binaryen.i32), ), ), module.br(loop_label_2), ]); const loop_2 = module.loop(loop_label_2, loop_stmts_2); const stmts_block_2 = module.block(block_label_2, [loop_init_2, loop_2]); statementArray.push(stmts_block_2); /**4. wrap the array with struct */ const arrayStructRef = binaryenCAPI._BinaryenStructNew( module.ptr, arrayToPtr([ module.local.get(resStrArrayIdx, stringArrayTypeInfo.typeRef), module.local.get(resArrLenIdx, binaryen.i32), ]).ptr, 2, stringArrayStructTypeInfo.heapTypeRef, ); statementArray.push(module.return(arrayStructRef)); const sliceBlock = module.block('split', statementArray); return sliceBlock; } function string_split_stringref(module: binaryen.Module) { const ref_index = 1; const sep_index = 2; const elems_index = 3; const searchBegIdx = 4; const matchIndexIdx = 5; const elems_len_index = 6; const sep_len_index = 7; const remain_str_index = 8; const ref = module.local.get( ref_index, binaryenCAPI._BinaryenTypeStringref(), ); const sep = module.local.get( sep_index, binaryenCAPI._BinaryenTypeStringref(), ); const elems = module.local.get(elems_index, stringrefArrayTypeInfo.typeRef); const searchBeg = module.local.get(searchBegIdx, binaryen.i32); const elems_len = module.local.get(elems_len_index, binaryen.i32); const sep_len = module.local.get(sep_len_index, binaryen.i32); const matchIndex = module.local.get(matchIndexIdx, binaryen.i32); const remain_str = module.local.get( remain_str_index, binaryenCAPI._BinaryenTypeStringref(), ); const statementArray: binaryen.ExpressionRef[] = []; statementArray.push(module.local.set(elems_len_index, module.i32.const(0))); statementArray.push( module.local.set( sep_len_index, binaryenCAPI._BinaryenStringMeasure( module.ptr, StringRefMeatureOp.WTF16, sep, ), ), ); // 1. get array length const block_label_1 = 'block_label_1'; const loop_label_1 = 'loop_block_1'; const loop_init_1 = module.local.set(searchBegIdx, module.i32.const(0)); const loop_stmts_1 = module.block(null, [ module.local.set( matchIndexIdx, module.call( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringIndexOfInternalFuncName, ), [ module.local.get(0, emptyStructType.typeRef), ref, sep, module.i32.const(1), searchBeg, ], binaryen.i32, ), ), module.local.set( elems_len_index, module.i32.add(elems_len, module.i32.const(1)), ), module.br( block_label_1, module.i32.eq(matchIndex, module.i32.const(-1)), ), module.local.set(searchBegIdx, module.i32.add(matchIndex, sep_len)), module.br(loop_label_1), ]); const loop_1 = module.loop(loop_label_1, loop_stmts_1); const stmts_block_1 = module.block(block_label_1, [loop_init_1, loop_1]); statementArray.push(stmts_block_1); // 2. create array statementArray.push( module.local.set( elems_index, binaryenCAPI._BinaryenArrayNew( module.ptr, stringrefArrayTypeInfo.heapTypeRef, elems_len, module.ref.null(binaryenCAPI._BinaryenTypeStringref()), ), ), ); // 3.split string by sep statementArray.push(module.local.set(remain_str_index, ref)); statementArray.push(module.local.set(elems_len_index, module.i32.const(0))); const block_label_2 = 'block_label_2'; const loop_label_2 = 'loop_block_2'; const loop_stmts_2 = module.block(null, [ module.local.set( matchIndexIdx, module.call( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringIndexOfInternalFuncName, ), [ module.local.get(0, emptyStructType.typeRef), remain_str, sep, module.i32.const(1), module.i32.const(0), ], binaryen.i32, ), ), module.br( block_label_2, module.i32.eq(matchIndex, module.i32.const(-1)), ), binaryenCAPI._BinaryenArraySet( module.ptr, elems, elems_len, binaryenCAPI._BinaryenStringSliceWTF( module.ptr, StringRefSliceOp.WTF16, binaryenCAPI._BinaryenStringAs( module.ptr, StringRefAsOp.WTF16, remain_str, ), module.i32.const(0), matchIndex, ), ), module.local.set( remain_str_index, binaryenCAPI._BinaryenStringSliceWTF( module.ptr, StringRefSliceOp.WTF16, binaryenCAPI._BinaryenStringAs( module.ptr, StringRefAsOp.WTF16, remain_str, ), module.i32.add(matchIndex, sep_len), module.i32.const(-1), ), ), module.local.set( elems_len_index, module.i32.add(elems_len, module.i32.const(1)), ), module.br(loop_label_2), ]); const loop_2 = module.loop(loop_label_2, loop_stmts_2); const stmts_block_2 = module.block(block_label_2, [loop_2]); statementArray.push(stmts_block_2); statementArray.push( binaryenCAPI._BinaryenArraySet( module.ptr, elems, elems_len, remain_str, ), ); // 4. return array const structArrayRef = binaryenCAPI._BinaryenStructNew( module.ptr, arrayToPtr([elems, module.i32.add(elems_len, module.i32.const(1))]).ptr, 2, stringrefArrayStructTypeInfo.heapTypeRef, ); statementArray.push(module.return(structArrayRef)); return module.block('split', statementArray); } function string_indexOf_internal(module: binaryen.Module) { /** Args: context, thisStr, pattern, begin Index*/ const thisStrStructIdx = 1; const patternStrIdx = 2; const beginIdx = 3; /* Locals: i, iend, j, len of this str, len of pattern str*/ const loopVarIIdx = 4; const loopVarIEndIdx = 5; const loopVarJIdx = 6; const thisStrLenIdx = 7; const patternLenIdx = 8; /* structure index informations*/ const arrayIdxInStruct = 1; const statementsArray: binaryen.ExpressionRef[] = []; /**0. get len of thisStr and patternStr*/ const thisStrStruct = module.local.get( thisStrStructIdx, stringTypeInfo.typeRef, ); const patternStrSturct = module.local.get( patternStrIdx, stringTypeInfo.typeRef, ); const thisStrArray = binaryenCAPI._BinaryenStructGet( module.ptr, arrayIdxInStruct, thisStrStruct, i8ArrayTypeInfo.typeRef, false, ); const patternStrArray = binaryenCAPI._BinaryenStructGet( module.ptr, arrayIdxInStruct, patternStrSturct, i8ArrayTypeInfo.typeRef, false, ); statementsArray.push( module.local.set( thisStrLenIdx, binaryenCAPI._BinaryenArrayLen(module.ptr, thisStrArray), ), module.local.set( patternLenIdx, binaryenCAPI._BinaryenArrayLen(module.ptr, patternStrArray), ), ); /** 1. get iend and set patternStrLen*/ statementsArray.push( module.local.set( loopVarIEndIdx, module.i32.sub( module.local.get(thisStrLenIdx, binaryen.i32), module.local.get(patternLenIdx, binaryen.i32), ), ), ); /** 2. Loop1 head line*/ const forLabel1 = 'for_loop_block1'; const forInit1 = module.local.set( loopVarIIdx, module.local.get(beginIdx, binaryen.i32), ); const forCondition1 = module.i32.le_s( module.local.get(loopVarIIdx, binaryen.i32), module.local.get(loopVarIEndIdx, binaryen.i32), ); const forIncrementor1 = module.local.set( loopVarIIdx, module.i32.add( module.local.get(loopVarIIdx, binaryen.i32), module.i32.const(1), ), ); /* 3. Loop2 headline*/ const forLabel2 = 'for_loop_2_block'; const forInit2 = module.local.set(loopVarJIdx, module.i32.const(0)); const forCondition2 = module.i32.lt_s( module.local.get(loopVarJIdx, binaryen.i32), module.local.get(patternLenIdx, binaryen.i32), ); const forIncrementor2 = module.local.set( loopVarJIdx, module.i32.add( module.local.get(loopVarJIdx, binaryen.i32), module.i32.const(1), ), ); const forLoop1Block1 = 'for_loop_1_Block_1'; /* 3.1 Loop2 body*/ const forBody2 = module.br( forLoop1Block1, module.i32.ne( binaryenCAPI._BinaryenArrayGet( module.ptr, patternStrArray, module.local.get(loopVarJIdx, binaryen.i32), i8ArrayTypeInfo.typeRef, false, ), binaryenCAPI._BinaryenArrayGet( module.ptr, thisStrArray, module.i32.add( module.local.get(loopVarJIdx, binaryen.i32), module.local.get(loopVarIIdx, binaryen.i32), ), i8ArrayTypeInfo.typeRef, false, ), ), ); const flattenLoop_2: FlattenLoop = { label: forLabel2, condition: forCondition2, statements: forBody2, incrementor: forIncrementor2, }; /**4 Loop1 body */ const forBody1Statements: binaryen.ExpressionRef[] = []; forBody1Statements.push(forInit2); forBody1Statements.push( module.block(forLoop1Block1, [ module.loop( forLabel2, FunctionalFuncs.flattenLoopStatement( module, flattenLoop_2, SemanticsKind.FOR, ), ), ]), ); forBody1Statements.push( module.if( module.i32.eq( module.local.get(loopVarJIdx, binaryen.i32), module.local.get(patternLenIdx, binaryen.i32), ), module.return(module.local.get(loopVarIIdx, binaryen.i32)), ), ); const flattenLoop_1: FlattenLoop = { label: forLabel1, condition: forCondition1, statements: module.block(null, forBody1Statements), incrementor: forIncrementor1, }; statementsArray.push( forInit1, module.loop( forLabel1, FunctionalFuncs.flattenLoopStatement( module, flattenLoop_1, SemanticsKind.FOR, ), ), ); /**5. default return -1*/ statementsArray.push(module.i32.const(-1)); const Block = module.block('indexOfInternal', statementsArray); return Block; } function string_indexOf(module: binaryen.Module) { /** Args: context, this, pattern*/ const thisStrStructIdx = 1; const paramStrIdx = 2; const statementArray: binaryen.ExpressionRef[] = []; /** call IndexofInternal and convert answer to f64 */ statementArray.push( module.f64.convert_s.i32( module.call( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringIndexOfInternalFuncName, ), [ module.local.get(0, emptyStructType.typeRef), module.local.get(thisStrStructIdx, stringTypeInfo.typeRef), module.local.get(paramStrIdx, stringTypeInfo.typeRef), module.i32.const(0), ], binaryen.i32, ), ), ); const Block = module.block('indexOf', statementArray); return Block; } function string_indexOf_internal_stringref(module: binaryen.Module) { const ref_index = 1; const str_index = 2; const start_from_front_index = 3; const loop_start_index = 4; const ref_len_index = 5; const str_len_index = 6; const loop_index = 7; const cur_index = 8; const end_index = 9; const res_index = 10; const ref = module.local.get( ref_index, binaryenCAPI._BinaryenTypeStringref(), ); const str = module.local.get( str_index, binaryenCAPI._BinaryenTypeStringref(), ); const start_from_front = module.local.get( start_from_front_index, binaryen.i32, ); const ref_len = module.local.get(ref_len_index, binaryen.i32); const str_len = module.local.get(str_len_index, binaryen.i32); const loop_value = module.local.get(loop_index, binaryen.i32); const cur = module.local.get( cur_index, binaryenCAPI._BinaryenTypeStringref(), ); const end = module.local.get(end_index, binaryen.i32); const res = module.local.get(res_index, binaryen.i32); const loop_start = module.local.get(loop_start_index, binaryen.i32); const statementArray: binaryen.ExpressionRef[] = []; statementArray.push(module.local.set(res_index, module.i32.const(-1))); statementArray.push(module.local.set(loop_index, loop_start)); statementArray.push( module.local.set( ref_len_index, binaryenCAPI._BinaryenStringMeasure( module.ptr, StringRefMeatureOp.WTF16, ref, ), ), ); statementArray.push( module.local.set( str_len_index, binaryenCAPI._BinaryenStringMeasure( module.ptr, StringRefMeatureOp.WTF16, str, ), ), ); statementArray.push( module.local.set( end_index, module.i32.add( module.i32.sub(ref_len, str_len), module.i32.const(1), ), ), ); const loopLabel = 'for_loop'; const loopCond = module.i32.lt_s(loop_value, end); const loopIncrementor = module.local.set( loop_index, module.i32.add(loop_value, module.i32.const(1)), ); const loopBody: binaryen.ExpressionRef[] = []; loopBody.push( module.local.set( cur_index, binaryenCAPI._BinaryenStringSliceWTF( module.ptr, StringRefSliceOp.WTF16, binaryenCAPI._BinaryenStringAs( module.ptr, StringRefAsOp.WTF16, ref, ), loop_value, module.i32.add(loop_value, str_len), ), ), ); const cond = binaryenCAPI._BinaryenStringEq( module.ptr, StringRefEqOp.EQ, cur, str, ); loopBody.push( module.if( cond, module.block(null, [ module.local.set(res_index, loop_value), module.if(start_from_front, module.return(res)), ]), ), ); const flattenLoop: FlattenLoop = { label: loopLabel, condition: loopCond, statements: module.block(null, loopBody), incrementor: loopIncrementor, }; statementArray.push( module.loop( loopLabel, FunctionalFuncs.flattenLoopStatement( module, flattenLoop, SemanticsKind.FOR, ), ), ); statementArray.push(module.return(res)); return module.block('indexOf', statementArray); } function string_indexOf_stringref(module: binaryen.Module) { const ref_index = 1; const str_index = 2; const statementArray: binaryen.ExpressionRef[] = []; const index = module.call( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringIndexOfInternalFuncName, ), [ module.local.get(0, emptyStructType.typeRef), module.local.get(ref_index, binaryenCAPI._BinaryenTypeStringref()), module.local.get(str_index, binaryenCAPI._BinaryenTypeStringref()), module.i32.const(1), module.i32.const(0), ], binaryen.i32, ); statementArray.push(module.return(module.f64.convert_s.i32(index))); return module.block('indexOf', statementArray); } function string_lastIndexOf_stringref(module: binaryen.Module) { const ref_index = 1; const str_index = 2; const statementArray: binaryen.ExpressionRef[] = []; const index = module.call( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringIndexOfInternalFuncName, ), [ module.local.get(0, emptyStructType.typeRef), module.local.get(ref_index, binaryenCAPI._BinaryenTypeStringref()), module.local.get(str_index, binaryenCAPI._BinaryenTypeStringref()), module.i32.const(0), module.i32.const(0), ], binaryen.i32, ); statementArray.push(module.return(module.f64.convert_s.i32(index))); return module.block('indexOf', statementArray); } function string_match(module: binaryen.Module) { /**Args: context, this, targetStr */ const thisStrStructIdx = 1; const targetStrStructIdx = 2; /**Locals */ // current matched index in source string const matchedPosIdx = 3; // the string array of result const resStrArrayIdx = 4; // current begining index for search in source string const searchBegIdx = 5; // current index where a matched word will be placed in the string array // currently, the string array contains no more than one element const curStrArrayIndexIdx = 6; // the string that stores matched word const tempCharArrayIdx = 7; // the length of matched word const curStrLenIdx = 8; // the length of pattern to be matched const targetStrLenIdx = 9; /**1. get targetStr */ const arrayIdxInStruct = 1; const targetStrStruct = module.local.get( targetStrStructIdx, stringTypeInfo.typeRef, ); const targetStrArray = binaryenCAPI._BinaryenStructGet( module.ptr, arrayIdxInStruct, targetStrStruct, i8ArrayTypeInfo.typeRef, false, ); const statementArray: binaryen.ExpressionRef[] = []; /**2. get length of target string */ const targetStrLen = binaryenCAPI._BinaryenArrayLen( module.ptr, targetStrArray, ); statementArray.push(module.local.set(targetStrLenIdx, targetStrLen)); /**3. create a string array and copy matched string to it*/ /**3.1 create a string array */ statementArray.push( module.local.set( resStrArrayIdx, binaryenCAPI._BinaryenArrayNew( module.ptr, stringArrayTypeInfo.heapTypeRef, module.i32.const(1), module.ref.null(stringTypeInfo.typeRef), ), ), ); /**3.2 find a matched string and copy it to resStrArr. Currently, only a single * matched string will be copied. */ const createNewCharArray = (module: binaryen.Module) => { return binaryenCAPI._BinaryenArrayNew( module.ptr, i8ArrayTypeInfo.heapTypeRef, module.local.get(curStrLenIdx, binaryen.i32), module.i32.const(0), ); }; const block_label_1 = 'block_label_1'; const loop_label_1 = 'loop_block_1'; const loop_init_1 = module.block(null, [ module.local.set(searchBegIdx, module.i32.const(0)), module.local.set(curStrArrayIndexIdx, module.i32.const(0)), ]); const loop_stmts_1 = module.block(null, [ module.local.set( matchedPosIdx, module.call( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringIndexOfInternalFuncName, ), [ module.local.get(0, emptyStructType.typeRef), module.local.get(thisStrStructIdx, stringTypeInfo.typeRef), module.local.get( targetStrStructIdx, stringTypeInfo.typeRef, ), module.local.get(searchBegIdx, binaryen.i32), ], binaryen.i32, ), ), module.if( module.i32.eq( module.local.get(matchedPosIdx, binaryen.i32), module.i32.const(-1), ), module.return(module.ref.null(stringArrayTypeInfo.typeRef)), module.local.set( curStrLenIdx, module.local.get(targetStrLenIdx, binaryen.i32), ), ), /** 3.2.1 create a char array */ module.local.set(tempCharArrayIdx, createNewCharArray(module)), /** 3.2.2 copy matched sub-string to char array */ binaryenCAPI._BinaryenArrayCopy( module.ptr, module.local.get(tempCharArrayIdx, i8ArrayTypeInfo.typeRef), module.i32.const(0), targetStrArray, module.i32.const(0), module.local.get(targetStrLenIdx, binaryen.i32), ), /** 3.2.3 place char array into string array */ binaryenCAPI._BinaryenArraySet( module.ptr, module.local.get(resStrArrayIdx, stringArrayTypeInfo.typeRef), module.local.get(curStrArrayIndexIdx, binaryen.i32), binaryenCAPI._BinaryenStructNew( module.ptr, arrayToPtr([ module.i32.const(0), module.local.get(tempCharArrayIdx, i8ArrayTypeInfo.typeRef), ]).ptr, 2, stringTypeInfo.heapTypeRef, ), ), /**3.3 inc the idx */ module.local.set( curStrArrayIndexIdx, module.i32.add( module.local.get(curStrArrayIndexIdx, binaryen.i32), module.i32.const(1), ), ), /**jump out the loop */ module.br( block_label_1, module.i32.eq(module.i32.const(1), module.i32.const(1)), ), /**jump to loop */ module.local.set( searchBegIdx, module.i32.add( module.local.get(matchedPosIdx, binaryen.i32), module.local.get(targetStrLenIdx, binaryen.i32), ), ), module.br(loop_label_1), ]); const loop_1 = module.loop(loop_label_1, loop_stmts_1); const stmts_block_1 = module.block(block_label_1, [loop_init_1, loop_1]); statementArray.push(stmts_block_1); const arrayStructRef = binaryenCAPI._BinaryenStructNew( module.ptr, arrayToPtr([ module.local.get(resStrArrayIdx, stringArrayTypeInfo.typeRef), module.i32.const(1), ]).ptr, 2, stringArrayStructTypeInfo.heapTypeRef, ); statementArray.push(module.return(arrayStructRef)); return module.block('match', statementArray); } // TODO: Now only return the first appreared pattern string function string_match_stringref(module: binaryen.Module) { const ref_index = 1; const str_index = 2; const match_pos_index = 3; const arr_index = 4; const ref = module.local.get( ref_index, binaryenCAPI._BinaryenTypeStringref(), ); const str = module.local.get( str_index, binaryenCAPI._BinaryenTypeStringref(), ); const match_pos = module.local.get(match_pos_index, binaryen.i32); const arr = module.local.get(arr_index, stringrefArrayTypeInfo.typeRef); const statementArray: binaryen.ExpressionRef[] = []; const getMatchedPos = module.local.set( match_pos_index, module.call( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringIndexOfInternalFuncName, ), [ module.local.get(0, emptyStructType.typeRef), ref, str, module.i32.const(1), module.i32.const(0), ], binaryen.i32, ), ); statementArray.push(getMatchedPos); statementArray.push( module.local.set( arr_index, binaryenCAPI._BinaryenArrayNew( module.ptr, stringrefArrayTypeInfo.heapTypeRef, module.i32.const(1), binaryenCAPI._BinaryenStringConst( module.ptr, UtilFuncs.getCString(''), ), ), ), ); statementArray.push( module.if( module.i32.eq(match_pos, module.i32.const(-1)), module.return( binaryenCAPI._BinaryenStructNew( module.ptr, arrayToPtr([arr, module.i32.const(1)]).ptr, 2, stringrefArrayStructTypeInfo.heapTypeRef, ), ), ), ); statementArray.push( binaryenCAPI._BinaryenArraySet( module.ptr, arr, module.i32.const(0), binaryenCAPI._BinaryenStringSliceWTF( module.ptr, StringRefSliceOp.WTF16, binaryenCAPI._BinaryenStringAs( module.ptr, StringRefAsOp.WTF16, ref, ), match_pos, module.i32.add( match_pos, binaryenCAPI._BinaryenStringMeasure( module.ptr, StringRefMeatureOp.WTF16, str, ), ), ), ), ); statementArray.push( module.return( binaryenCAPI._BinaryenStructNew( module.ptr, arrayToPtr([arr, module.i32.const(1)]).ptr, 2, stringrefArrayStructTypeInfo.heapTypeRef, ), ), ); return module.block('match', statementArray); } function string_search(module: binaryen.Module) { /**Args: context, this, pattern */ const thisStrStructIdx = 1; const targetStrStructIdx = 2; /**Locals */ // 1.index of matched position const matchedPosIdx = 3; const statementArray: binaryen.ExpressionRef[] = []; const findPattern = module.block(null, [ module.local.set( matchedPosIdx, module.call( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringIndexOfInternalFuncName, ), [ module.local.get(0, emptyStructType.typeRef), module.local.get(thisStrStructIdx, stringTypeInfo.typeRef), module.local.get( targetStrStructIdx, stringTypeInfo.typeRef, ), module.i32.const(0), ], binaryen.i32, ), ), module.return( module.f64.convert_s.i32( module.local.get(matchedPosIdx, binaryen.i32), ), ), ]); statementArray.push(findPattern); return module.block('search', statementArray); } // TODO: Now it works as string.indexOf function string_search_stringref(module: binaryen.Module) { const ref_index = 1; const str_index = 2; const statementArray: binaryen.ExpressionRef[] = []; const index = module.call( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringIndexOfInternalFuncName, ), [ module.local.get(0, emptyStructType.typeRef), module.local.get(ref_index, binaryenCAPI._BinaryenTypeStringref()), module.local.get(str_index, binaryenCAPI._BinaryenTypeStringref()), module.i32.const(1), module.i32.const(0), ], binaryen.i32, ); statementArray.push(module.return(module.f64.convert_s.i32(index))); return module.block('search', statementArray); } function string_charAt(module: binaryen.Module) { const statementArray: binaryen.ExpressionRef[] = []; /** Args: context, this, index */ const thisStrStructIdx = 1; const paramIdx = 2; const newStrArrayIdx = 3; const thisStrStruct = module.local.get( thisStrStructIdx, stringTypeInfo.typeRef, ); const strArray = binaryenCAPI._BinaryenStructGet( module.ptr, 1, thisStrStruct, i8ArrayTypeInfo.typeRef, false, ); const strLen = binaryenCAPI._BinaryenArrayLen(module.ptr, strArray); const index = module.i32.trunc_s.f64( module.local.get(paramIdx, binaryen.f64), ); const judgment = module.if( module.i32.ge_s(index, module.i32.const(0)), module.if( module.i32.le_s(index, module.i32.sub(strLen, module.i32.const(1))), module.block(null, [ module.local.set( newStrArrayIdx, binaryenCAPI._BinaryenArrayNew( module.ptr, i8ArrayTypeInfo.heapTypeRef, module.i32.const(1), module.i32.const(0), ), ), binaryenCAPI._BinaryenArrayCopy( module.ptr, module.local.get(newStrArrayIdx, i8ArrayTypeInfo.typeRef), module.i32.const(0), strArray, index, module.i32.const(1), ), module.return( binaryenCAPI._BinaryenStructNew( module.ptr, arrayToPtr([ module.i32.const(0), module.local.get( newStrArrayIdx, i8ArrayTypeInfo.typeRef, ), ]).ptr, 2, stringTypeInfo.heapTypeRef, ), ), ]), module.block(null, [ module.local.set( newStrArrayIdx, binaryenCAPI._BinaryenArrayNew( module.ptr, i8ArrayTypeInfo.heapTypeRef, module.i32.const(0), module.i32.const(0), ), ), module.return( binaryenCAPI._BinaryenStructNew( module.ptr, arrayToPtr([ module.i32.const(0), module.local.get( newStrArrayIdx, i8ArrayTypeInfo.typeRef, ), ]).ptr, 2, stringTypeInfo.heapTypeRef, ), ), ]), ), module.block(null, [ module.local.set( newStrArrayIdx, binaryenCAPI._BinaryenArrayNew( module.ptr, i8ArrayTypeInfo.heapTypeRef, module.i32.const(0), module.i32.const(0), ), ), module.return( binaryenCAPI._BinaryenStructNew( module.ptr, arrayToPtr([ module.i32.const(0), module.local.get( newStrArrayIdx, i8ArrayTypeInfo.typeRef, ), ]).ptr, 2, stringTypeInfo.heapTypeRef, ), ), ]), ); statementArray.push(judgment); /* generate block, return block */ statementArray.push(module.return(thisStrStruct)); const charAtBlock = module.block('string_charAt', statementArray); return charAtBlock; } function string_charAt_stringref(module: binaryen.Module) { const ref_index = 1; const index_index = 2; const index_i32_index = 3; const len_index = 4; const ref = module.local.get( ref_index, binaryenCAPI._BinaryenTypeStringref(), ); const index_i32 = module.local.get(index_i32_index, binaryen.i32); const len = module.local.get(len_index, binaryen.i32); const statementArray: binaryen.ExpressionRef[] = []; statementArray.push( module.local.set( index_i32_index, module.i32.trunc_s_sat.f64( module.local.get(index_index, binaryen.f64), ), ), ); statementArray.push( module.local.set( len_index, binaryenCAPI._BinaryenStringMeasure( module.ptr, StringRefMeatureOp.WTF16, ref, ), ), ); statementArray.push( module.if( module.i32.or( module.i32.lt_s(index_i32, module.i32.const(0)), module.i32.ge_s(index_i32, len), ), module.return( binaryenCAPI._BinaryenStringConst( module.ptr, UtilFuncs.getCString(''), ), ), ), ); statementArray.push( module.return( binaryenCAPI._BinaryenStringSliceWTF( module.ptr, StringRefSliceOp.WTF16, binaryenCAPI._BinaryenStringAs( module.ptr, StringRefAsOp.WTF16, ref, ), index_i32, module.i32.add(index_i32, module.i32.const(1)), ), ), ); return module.block('string_charAt', statementArray); } function string_substring_stringref(module: binaryen.Module) { const ref_index = 1; const start_index = 2; const end_index = 3; const start_i32_index = 4; const end_i32_index = 5; const len_index = 6; const temp_index = 7; // swap when start < end, like substring(3, 1) const ref = module.local.get( ref_index, binaryenCAPI._BinaryenTypeStringref(), ); const start = module.local.get(start_index, binaryen.f64); const end = module.local.get(end_index, binaryen.anyref); const start_i32 = module.local.get(start_i32_index, binaryen.i32); const end_i32 = module.local.get(end_i32_index, binaryen.i32); const len = module.local.get(len_index, binaryen.i32); const temp = module.local.get(temp_index, binaryen.i32); const statementArray: binaryen.ExpressionRef[] = []; statementArray.push( module.local.set( len_index, binaryenCAPI._BinaryenStringMeasure( module.ptr, StringRefMeatureOp.WTF16, ref, ), ), ); statementArray.push( module.local.set(start_i32_index, module.i32.trunc_s_sat.f64(start)), ); statementArray.push( module.if( module.i32.lt_s(start_i32, module.i32.const(0)), module.local.set(start_i32_index, module.i32.const(0)), module.if( module.i32.ge_s(start_i32, len), module.local.set(start_i32_index, len), ), ), ); const isUndefined = FunctionalFuncs.isBaseType( module, end, dyntype.dyntype_is_undefined, ); const num = module.i32.trunc_s_sat.f64( module.call( dyntype.dyntype_to_number, [FunctionalFuncs.getDynContextRef(module), end], binaryen.f64, ), ); statementArray.push( module.if( isUndefined, module.local.set(end_i32_index, len), module.local.set(end_i32_index, num), ), ); statementArray.push( module.if( module.i32.lt_s(end_i32, module.i32.const(0)), module.local.set(end_i32_index, module.i32.const(0)), module.if( module.i32.ge_s(end_i32, len), module.local.set(end_i32_index, len), ), ), ); statementArray.push( module.if( module.i32.gt_s(start_i32, end_i32), module.block(null, [ module.local.set(temp_index, start_i32), module.local.set(start_i32_index, end_i32), module.local.set(end_i32_index, temp), ]), ), ); statementArray.push( module.return( binaryenCAPI._BinaryenStringSliceWTF( module.ptr, StringRefSliceOp.WTF16, binaryenCAPI._BinaryenStringAs( module.ptr, StringRefAsOp.WTF16, ref, ), start_i32, end_i32, ), ), ); return module.block('substring', statementArray); } function string_charCodeAt_stringref(module: binaryen.Module) { const ref_index = 1; const pos_index = 2; const pos_i32_index = 3; const len_index = 4; const char_code_index = 5; const ref = module.local.get( ref_index, binaryenCAPI._BinaryenTypeStringref(), ); const pos = module.local.get(pos_index, binaryen.f64); const pos_i32 = module.local.get(pos_i32_index, binaryen.i32); const len = module.local.get(len_index, binaryen.i32); const char_code = module.local.get(char_code_index, binaryen.i32); const statementArray: binaryen.ExpressionRef[] = []; statementArray.push( module.local.set( len_index, binaryenCAPI._BinaryenStringMeasure( module.ptr, StringRefMeatureOp.WTF16, ref, ), ), ); statementArray.push( module.local.set(pos_i32_index, module.i32.trunc_s_sat.f64(pos)), ); statementArray.push( module.if( module.i32.or( module.i32.lt_s(pos_i32, module.i32.const(0)), module.i32.ge_s(pos_i32, len), ), module.return(module.f64.const(NaN)), ), ); statementArray.push( module.local.set( char_code_index, binaryenCAPI._BinaryenStringWTF16Get( module.ptr, binaryenCAPI._BinaryenStringAs( module.ptr, StringRefAsOp.WTF16, ref, ), pos_i32, ), ), ); statementArray.push(module.return(module.f64.convert_s.i32(char_code))); return module.block('charCodeAt', statementArray); } function string_toLowerCase(module: binaryen.Module) { return string_toLowerOrUpperCase_internal(module, true); } function string_toLowerCase_stringref(module: binaryen.Module) { return module.unreachable(); } function string_toUpperCase(module: binaryen.Module) { return string_toLowerOrUpperCase_internal(module, false); } function string_toUpperCase_stringref(module: binaryen.Module) { return module.unreachable(); } function string_toLowerOrUpperCase_internal( module: binaryen.Module, lower: boolean, ) { const statementArray: binaryen.ExpressionRef[] = []; const strIdx = 1; const for_i_Idx = 2; const newStrArrayIdx = 3; const copyCurLenIdx = 4; const paramIdx = 5; const str = module.local.get(strIdx, stringTypeInfo.typeRef); const newStrArrayType = i8ArrayTypeInfo.typeRef; const thisStrArray = binaryenCAPI._BinaryenStructGet( module.ptr, 1, str, i8ArrayTypeInfo.typeRef, false, ); const thisStrLen = binaryenCAPI._BinaryenArrayLen(module.ptr, thisStrArray); if (lower == true) { statementArray.push(module.local.set(paramIdx, module.i32.const(1))); } else { statementArray.push(module.local.set(paramIdx, module.i32.const(0))); } statementArray.push( module.local.set( newStrArrayIdx, binaryenCAPI._BinaryenArrayNew( module.ptr, i8ArrayTypeInfo.heapTypeRef, thisStrLen, module.i32.const(0), ), ), ); statementArray.push( binaryenCAPI._BinaryenArrayCopy( module.ptr, module.local.get(newStrArrayIdx, i8ArrayTypeInfo.typeRef), module.i32.const(0), thisStrArray, module.i32.const(0), thisStrLen, ), ); statementArray.push(module.local.set(copyCurLenIdx, thisStrLen)); const strArray = binaryenCAPI._BinaryenStructNew( module.ptr, arrayToPtr([ module.i32.const(0), module.local.get(newStrArrayIdx, newStrArrayType), ]).ptr, 2, stringTypeInfo.heapTypeRef, ); const strLen = binaryenCAPI._BinaryenArrayLen(module.ptr, thisStrArray); const for_label_1 = 'for_loop_1_block'; const for_init_1 = module.local.set(for_i_Idx, module.i32.const(0)); const for_condition_1 = module.i32.lt_u( module.local.get(for_i_Idx, binaryen.i32), strLen, ); const for_incrementor_1 = module.local.set( for_i_Idx, module.i32.add( module.local.get(for_i_Idx, binaryen.i32), module.i32.const(1), ), ); const for_body_1 = module.if( module.i32.eq( module.local.get(paramIdx, binaryen.i32), module.i32.const(1), ), module.if( module.i32.ge_u( binaryenCAPI._BinaryenArrayGet( module.ptr, module.local.get(newStrArrayIdx, i8ArrayTypeInfo.typeRef), module.local.get(for_i_Idx, binaryen.i32), i8ArrayTypeInfo.typeRef, false, ), module.i32.const(65), ), module.if( module.i32.le_u( binaryenCAPI._BinaryenArrayGet( module.ptr, module.local.get( newStrArrayIdx, i8ArrayTypeInfo.typeRef, ), module.local.get(for_i_Idx, binaryen.i32), i8ArrayTypeInfo.typeRef, false, ), module.i32.const(90), ), binaryenCAPI._BinaryenArraySet( module.ptr, module.local.get(newStrArrayIdx, i8ArrayTypeInfo.typeRef), module.local.get(for_i_Idx, binaryen.i32), module.i32.add( binaryenCAPI._BinaryenArrayGet( module.ptr, module.local.get( newStrArrayIdx, i8ArrayTypeInfo.typeRef, ), module.local.get(for_i_Idx, binaryen.i32), i8ArrayTypeInfo.typeRef, false, ), module.i32.const(32), ), ), ), ), module.if( module.i32.ge_u( binaryenCAPI._BinaryenArrayGet( module.ptr, module.local.get(newStrArrayIdx, i8ArrayTypeInfo.typeRef), module.local.get(for_i_Idx, binaryen.i32), i8ArrayTypeInfo.typeRef, false, ), module.i32.const(97), ), module.if( module.i32.le_u( binaryenCAPI._BinaryenArrayGet( module.ptr, module.local.get( newStrArrayIdx, i8ArrayTypeInfo.typeRef, ), module.local.get(for_i_Idx, binaryen.i32), i8ArrayTypeInfo.typeRef, false, ), module.i32.const(122), ), binaryenCAPI._BinaryenArraySet( module.ptr, module.local.get(newStrArrayIdx, i8ArrayTypeInfo.typeRef), module.local.get(for_i_Idx, binaryen.i32), module.i32.sub( binaryenCAPI._BinaryenArrayGet( module.ptr, module.local.get( newStrArrayIdx, i8ArrayTypeInfo.typeRef, ), module.local.get(for_i_Idx, binaryen.i32), i8ArrayTypeInfo.typeRef, false, ), module.i32.const(32), ), ), ), ), ); const flattenLoop_1: FlattenLoop = { label: for_label_1, condition: for_condition_1, statements: for_body_1, incrementor: for_incrementor_1, }; statementArray.push(for_init_1); statementArray.push( module.loop( for_label_1, FunctionalFuncs.flattenLoopStatement( module, flattenLoop_1, SemanticsKind.FOR, ), ), ); statementArray.push(module.return(strArray)); const toLowerOrUpperCaseinternalBlock = module.block( 'toLowerOrUpperCaseinternal', statementArray, ); return toLowerOrUpperCaseinternalBlock; } function string_trim(module: binaryen.Module) { const thisStrStructIdx = 1; const for_i_Idx = 2; const newStrCharArrayIdx = 3; const trimStartIdx = 4; const trimEndIdx = 5; const thisStrStruct = module.local.get( thisStrStructIdx, stringTypeInfo.typeRef, ); const thisStrCharArray = binaryenCAPI._BinaryenStructGet( module.ptr, 1, thisStrStruct, i8ArrayTypeInfo.typeRef, false, ); const thisStrLen = binaryenCAPI._BinaryenArrayLen( module.ptr, thisStrCharArray, ); const statementArray: binaryen.ExpressionRef[] = []; /**if the array is empty */ const stringIsEmpty = module.if( module.i32.eq(thisStrLen, module.i32.const(0)), module.return(thisStrStruct), ); statementArray.push(stringIsEmpty); /**loop1 */ const block_label_1 = 'block_label_1'; const loop_label_1 = 'loop_label_1'; const loop_init_1 = module.block(null, [ module.local.set(trimStartIdx, module.i32.const(0)), module.local.set(for_i_Idx, module.i32.const(0)), ]); const loop_stmts_1 = module.block(null, [ module.if( module.i32.ne( binaryenCAPI._BinaryenArrayGet( module.ptr, thisStrCharArray, module.local.get(for_i_Idx, binaryen.i32), i8ArrayTypeInfo.typeRef, false, ), module.i32.const(32), ), module.block(null, [ module.local.set( trimStartIdx, module.local.get(for_i_Idx, binaryen.i32), ), /**jump out the loop */ module.br(block_label_1), ]), ), /**inc i */ module.local.set( for_i_Idx, module.i32.add( module.local.get(for_i_Idx, binaryen.i32), module.i32.const(1), ), ), /**jump out the loop */ module.br( block_label_1, module.i32.ge_s( module.local.get(for_i_Idx, binaryen.i32), thisStrLen, ), ), /**jump to loop */ module.br(loop_label_1), ]); const loop_1 = module.loop(loop_label_1, loop_stmts_1); const stmts_block_1 = module.block(block_label_1, [loop_init_1, loop_1]); statementArray.push(stmts_block_1); /**loop2 */ const block_label_2 = 'block_label_2'; const loop_label_2 = 'loop_label_2'; const loop_init_2 = module.block(null, [ module.local.set( trimEndIdx, module.i32.sub(thisStrLen, module.i32.const(1)), ), module.local.set( for_i_Idx, module.i32.sub(thisStrLen, module.i32.const(1)), ), ]); const loop_stmts_2 = module.block(null, [ module.if( module.i32.ne( binaryenCAPI._BinaryenArrayGet( module.ptr, thisStrCharArray, module.local.get(for_i_Idx, binaryen.i32), i8ArrayTypeInfo.typeRef, false, ), module.i32.const(32), ), module.block(null, [ module.local.set( trimEndIdx, module.local.get(for_i_Idx, binaryen.i32), ), /**jump out the loop */ module.br(block_label_2), ]), ), /**dec i */ module.local.set( for_i_Idx, module.i32.sub( module.local.get(for_i_Idx, binaryen.i32), module.i32.const(1), ), ), /**jump out the loop */ module.br( block_label_2, module.i32.le_s( module.local.get(for_i_Idx, binaryen.i32), module.i32.const(-1), ), ), /**jump to loop */ module.br(loop_label_2), ]); const loop_2 = module.loop(loop_label_2, loop_stmts_2); const stmts_block_2 = module.block(block_label_2, [loop_init_2, loop_2]); statementArray.push(stmts_block_2); /**copy the array between trimStart and trimEnd */ const newStrLen = module.i32.add( module.i32.sub( module.local.get(trimEndIdx, binaryen.i32), module.local.get(trimStartIdx, binaryen.i32), ), module.i32.const(1), ); statementArray.push( module.local.set( newStrCharArrayIdx, binaryenCAPI._BinaryenArrayNew( module.ptr, i8ArrayTypeInfo.heapTypeRef, newStrLen, module.i32.const(0), ), ), ); statementArray.push( binaryenCAPI._BinaryenArrayCopy( module.ptr, module.local.get(newStrCharArrayIdx, i8ArrayTypeInfo.typeRef), module.i32.const(0), thisStrCharArray, module.local.get(trimStartIdx, binaryen.i32), newStrLen, ), ); statementArray.push( module.return( binaryenCAPI._BinaryenStructNew( module.ptr, arrayToPtr([ module.i32.const(0), module.local.get( newStrCharArrayIdx, i8ArrayTypeInfo.typeRef, ), ]).ptr, 2, stringTypeInfo.heapTypeRef, ), ), ); const trimBlock = module.block('string_trim', statementArray); return trimBlock; } function string_trim_stringref(module: binaryen.Module) { const ref_index = 1; const start_index = 2; const end_index = 3; const len_index = 4; const ref = module.local.get( ref_index, binaryenCAPI._BinaryenTypeStringref(), ); const start = module.local.get(start_index, binaryen.i32); const end = module.local.get(end_index, binaryen.i32); const len = module.local.get(len_index, binaryen.i32); const statementArray: binaryen.ExpressionRef[] = []; statementArray.push( module.local.set( len_index, binaryenCAPI._BinaryenStringMeasure( module.ptr, StringRefMeatureOp.WTF16, ref, ), ), ); statementArray.push( module.if(module.i32.eq(len, module.i32.const(0)), module.return(ref)), ); statementArray.push(module.local.set(start_index, module.i32.const(0))); statementArray.push( module.local.set(end_index, module.i32.sub(len, module.i32.const(1))), ); let loop_index = 0; const while_loop = ( indexRef: binaryenCAPI.ExpressionRef, index: number, boundRef: binaryenCAPI.ExpressionRef, from_start: boolean, ) => { const char = module.call( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringcharAtFuncName, ), [ module.local.get(0, emptyStructType.typeRef), ref, module.f64.convert_u.i32(indexRef), ], binaryenCAPI._BinaryenTypeStringref(), ); const accessBound = from_start ? module.i32.le_u(indexRef, boundRef) : module.i32.ge_u(indexRef, boundRef); const loopLabel = `while_loop_${loop_index++}`; const loopCond = module.i32.and( accessBound, binaryenCAPI._BinaryenStringEq( module.ptr, StringRefEqOp.EQ, char, binaryenCAPI._BinaryenStringConst( module.ptr, UtilFuncs.getCString(' '), ), ), ); const loopStmts = module.block(null, [ module.local.set( index, from_start ? module.i32.add(indexRef, module.i32.const(1)) : module.i32.sub(indexRef, module.i32.const(1)), ), ]); const flattenLoop: FlattenLoop = { label: loopLabel, condition: loopCond, statements: loopStmts, }; return module.loop( loopLabel, FunctionalFuncs.flattenLoopStatement( module, flattenLoop, SemanticsKind.WHILE, ), ); }; statementArray.push(while_loop(start, start_index, end, true)); statementArray.push(while_loop(end, end_index, start, false)); statementArray.push( binaryenCAPI._BinaryenStringSliceWTF( module.ptr, StringRefSliceOp.WTF16, binaryenCAPI._BinaryenStringAs( module.ptr, StringRefAsOp.WTF16, ref, ), start, module.i32.add(end, module.i32.const(1)), ), ); return module.block('string_trim', statementArray); } function Array_isArray(module: binaryen.Module) { /** Args: context, this, any */ /* workaround: interface's method has the @this param */ const paramAnyIdx = 2; /** Locals: returnIdx */ const returnIdx = 3; const param = module.local.get(paramAnyIdx, binaryen.anyref); const statementArray: binaryen.ExpressionRef[] = []; const setDefault = module.local.set(returnIdx, module.i32.const(0)); const setTrue = module.local.set(returnIdx, module.i32.const(1)); const returnStmt = module.return(module.local.get(returnIdx, binaryen.i32)); const dynTypeIsArray = module.call( dyntype.dyntype_is_array, [module.global.get(dyntype.dyntype_context, dyntype.dyn_ctx_t), param], dyntype.bool, ); const is_array = module.if( module.i32.eq(dynTypeIsArray, dyntype.bool_true), setTrue, ); const is_arr_extref = module.call( dyntype.dyntype_typeof1, [ module.global.get(dyntype.dyntype_context, dyntype.dyn_value_t), param, ], binaryen.i32, ); const is_any_array = module.if( module.i32.eq( module.local.get(returnIdx, binaryen.i32), module.i32.const(0), ), /* tag `DynExtRefArray` in runtime library's enum `dyn_type_t` value is 12 */ module.if(module.i32.eq(is_arr_extref, module.i32.const(12)), setTrue), ); statementArray.push(setDefault); statementArray.push(is_array); statementArray.push(is_any_array); statementArray.push(returnStmt); return module.block(null, statementArray); } function allocExtRefTableSlot(module: binaryen.Module) { const objIdx = 0; const tableIdx = 1; const loopIdx = 2; const tmpMaskArrIdx = 3; const arrName = getBuiltInFuncName(BuiltinNames.extRefTableMaskArr); const maskArr = binaryenCAPI._BinaryenGlobalGet( module.ptr, UtilFuncs.getCString(arrName), i8ArrayTypeInfo.typeRef, ); const newArray = binaryenCAPI._BinaryenArrayNew( module.ptr, i8ArrayTypeInfo.heapTypeRef, binaryenCAPI._BinaryenTableSize( module.ptr, UtilFuncs.getCString(BuiltinNames.extrefTable), ), module.i32.const(0), ); const tableGrow = binaryenCAPI._BinaryenTableGrow( module.ptr, UtilFuncs.getCString(BuiltinNames.extrefTable), FunctionalFuncs.getEmptyRef(module), module.i32.const(BuiltinNames.tableGrowDelta), ); const stmts: binaryen.ExpressionRef[] = []; stmts.push(module.local.set(tableIdx, module.i32.const(-1))); stmts.push(module.local.set(loopIdx, module.i32.const(0))); stmts.push( module.if( module.ref.is_null(maskArr), module.block(null, [ tableGrow, binaryenCAPI._BinaryenGlobalSet( module.ptr, UtilFuncs.getCString(arrName), newArray, ), ]), ), ); const maskArrLen = binaryenCAPI._BinaryenArrayLen(module.ptr, maskArr); const loopBlock = 'look_block'; const loopLabel = 'for_loop'; const loopBlockStmts: binaryen.ExpressionRef[] = []; const loopStmts: binaryen.ExpressionRef[] = []; const loopCond = module.i32.lt_u( module.local.get(loopIdx, binaryen.i32), maskArrLen, ); const ifBlockStmts: binaryen.ExpressionRef[] = []; ifBlockStmts.push( module.if( module.i32.eq( binaryenCAPI._BinaryenArrayGet( module.ptr, maskArr, module.local.get(loopIdx, binaryen.i32), i8ArrayTypeInfo.typeRef, false, ), module.i32.const(0), ), module.block(null, [ module.local.set( tableIdx, module.local.get(loopIdx, binaryen.i32), ), module.br(loopBlock), ]), ), ); loopStmts.push(module.block(null, ifBlockStmts)); loopStmts.push( module.local.set( loopIdx, module.i32.add( module.local.get(loopIdx, binaryen.i32), module.i32.const(1), ), ), ); loopStmts.push(module.br(loopLabel)); loopBlockStmts.push( module.loop( loopLabel, module.if(loopCond, module.block(null, loopStmts)), ), ); stmts.push(module.block(loopBlock, loopBlockStmts)); const ifStmts2: binaryen.ExpressionRef[] = []; ifStmts2.push(tableGrow); ifStmts2.push(module.local.set(tableIdx, maskArrLen)); ifStmts2.push(module.local.set(tmpMaskArrIdx, newArray)); ifStmts2.push( binaryenCAPI._BinaryenArrayCopy( module.ptr, module.local.get(tmpMaskArrIdx, i8ArrayTypeInfo.typeRef), module.i32.const(0), maskArr, module.i32.const(0), maskArrLen, ), ); ifStmts2.push( binaryenCAPI._BinaryenGlobalSet( module.ptr, UtilFuncs.getCString(arrName), module.local.get(tmpMaskArrIdx, i8ArrayTypeInfo.typeRef), ), ); stmts.push( module.if( module.i32.eq( module.local.get(tableIdx, binaryen.i32), module.i32.const(-1), ), module.block(null, ifStmts2), ), ); const tableSetOp = binaryenCAPI._BinaryenTableSet( module.ptr, UtilFuncs.getCString(BuiltinNames.extrefTable), module.local.get(tableIdx, binaryen.i32), module.local.get(objIdx, binaryen.anyref), ); stmts.push(tableSetOp); stmts.push( binaryenCAPI._BinaryenArraySet( module.ptr, maskArr, module.local.get(tableIdx, binaryen.i32), module.i32.const(1), ), ); stmts.push(module.return(module.local.get(tableIdx, binaryen.i32))); return module.block(null, stmts); } /** to extref with runtime getting table index */ function newExtRef(module: binaryen.Module) { const _context_unused = 0; const objTagIdx = 1; const objIdx = 2; /* alloc table slot */ const tableIdx = module.call( getBuiltInFuncName(BuiltinNames.allocExtRefTableSlot), [module.local.get(objIdx, binaryen.anyref)], dyntype.int, ); /* create extref */ const call = module.call( dyntype.dyntype_new_extref, [ binaryenCAPI._BinaryenGlobalGet( module.ptr, UtilFuncs.getCString(dyntype.dyntype_context), binaryen.anyref, ), tableIdx, module.local.get(objTagIdx, binaryen.i32), ], dyntype.dyn_value_t, ); return module.return(call); } function getPropertyIfTypeIdMismatch(module: binaryen.Module) { /* params */ const flagAndIndex_Idx = 0; const infcPropTypeId_Idx = 1; const objPropTypeId_Idx = 2; const objRef_Idx = 3; /* locals */ const flag_Idx = 4; const index_Idx = 5; const anyTypedRes_Idx = 6; const stmts = [ module.local.set( flag_Idx, FunctionalFuncs.getPropFlagFromObj( module, module.local.get(flagAndIndex_Idx, binaryen.i32), ), ), module.local.set( index_Idx, FunctionalFuncs.getPropIndexFromObj( module, module.local.get(flagAndIndex_Idx, binaryen.i32), ), ), ]; const objPropTypeIdRefValue = module.local.get( objPropTypeId_Idx, binaryen.i32, ); const objRefValue = module.local.get(objRef_Idx, binaryen.anyref); const i32IdxRefValue = module.local.get(index_Idx, binaryen.i32); const ifPropertyUnExist = FunctionalFuncs.isPropertyUnExist( module, module.local.get(flagAndIndex_Idx, binaryen.i32), ); const setResAsUndefined = module.local.set( anyTypedRes_Idx, FunctionalFuncs.generateDynUndefined(module), ); const ifInfcPropIsAny = FunctionalFuncs.isPropTypeIdEqual( module, module.local.get(infcPropTypeId_Idx, binaryen.i32), module.i32.const(PredefinedTypeId.ANY), ); const ifInfcPropIsAnyTrue = generateSwitchBlock( module, objPropTypeIdRefValue, anyTypedRes_Idx, objRefValue, i32IdxRefValue, ); const ifObjPropIsAny = FunctionalFuncs.isPropTypeIdEqual( module, objPropTypeIdRefValue, module.i32.const(PredefinedTypeId.ANY), ); const ifPropertyExistTrue = module.if( ifInfcPropIsAny, ifInfcPropIsAnyTrue, module.if( ifObjPropIsAny, module.local.set( anyTypedRes_Idx, module.call( structdyn.StructDyn.struct_get_indirect_anyref, [objRefValue, i32IdxRefValue], binaryen.anyref, ), ), setResAsUndefined, ), ); stmts.push( module.if(ifPropertyUnExist, setResAsUndefined, ifPropertyExistTrue), ); stmts.push( module.return(module.local.get(anyTypedRes_Idx, binaryen.anyref)), ); return module.block(null, stmts, binaryen.anyref); } function setPropertyIfTypeIdMismatch(module: binaryen.Module) { /* params */ const flagAndIndex_Idx = 0; const targetValueTypeId_Idx = 1; const objPropTypeId_Idx = 2; const objRef_Idx = 3; const targetValueRef_Idx = 4; /* locals */ const flag_Idx = 5; const index_Idx = 6; const stmts = [ module.local.set( flag_Idx, FunctionalFuncs.getPropFlagFromObj( module, module.local.get(flagAndIndex_Idx, binaryen.i32), ), ), module.local.set( index_Idx, FunctionalFuncs.getPropIndexFromObj( module, module.local.get(flagAndIndex_Idx, binaryen.i32), ), ), ]; const ifPropertyUnExist = FunctionalFuncs.isPropertyUnExist( module, module.local.get(flagAndIndex_Idx, binaryen.i32), ); const ifObjPropIsAny = FunctionalFuncs.isPropTypeIdEqual( module, module.local.get(objPropTypeId_Idx, binaryen.i32), module.i32.const(PredefinedTypeId.ANY), ); const ifTargetValueIsAny = FunctionalFuncs.isPropTypeIdEqual( module, module.local.get(targetValueTypeId_Idx, binaryen.i32), module.i32.const(PredefinedTypeId.ANY), ); const targetValueIsAnyBranches: binaryen.ExpressionRef[] = new Array(4); targetValueIsAnyBranches[0] = module.br( 'case_obj_prop_type_is_number', FunctionalFuncs.isPropTypeIdEqual( module, module.local.get(targetValueTypeId_Idx, binaryen.i32), module.i32.const(PredefinedTypeId.NUMBER), ), ); targetValueIsAnyBranches[1] = module.br( 'case_obj_prop_type_is_boolean', FunctionalFuncs.isPropTypeIdEqual( module, module.local.get(targetValueTypeId_Idx, binaryen.i32), module.i32.const(PredefinedTypeId.BOOLEAN), ), ); targetValueIsAnyBranches[2] = module.br( 'case_obj_prop_type_is_string', FunctionalFuncs.isPropTypeIdEqual( module, module.local.get(targetValueTypeId_Idx, binaryen.i32), module.i32.const(PredefinedTypeId.STRING), ), ); targetValueIsAnyBranches[3] = module.br('obj_prop_type_default'); let targetValueIsAnyBlock = module.block( 'case_obj_prop_type_is_number', targetValueIsAnyBranches, ); targetValueIsAnyBlock = module.block( 'case_obj_prop_type_is_boolean', [targetValueIsAnyBlock].concat( module.call( structdyn.StructDyn.struct_set_indirect_f64, [ module.local.get(objRef_Idx, binaryen.anyref), module.local.get(index_Idx, binaryen.i32), FunctionalFuncs.unboxAnyToBase( module, module.local.get(targetValueRef_Idx, binaryen.anyref), ValueTypeKind.NUMBER, ), ], binaryen.none, ), module.br('obj_prop_type_break'), ), ); targetValueIsAnyBlock = module.block( 'case_obj_prop_type_is_string', [targetValueIsAnyBlock].concat( module.call( structdyn.StructDyn.struct_set_indirect_i32, [ module.local.get(objRef_Idx, binaryen.anyref), module.local.get(index_Idx, binaryen.i32), FunctionalFuncs.unboxAnyToBase( module, module.local.get(targetValueRef_Idx, binaryen.anyref), ValueTypeKind.BOOLEAN, ), ], binaryen.none, ), module.br('obj_prop_type_break'), ), ); targetValueIsAnyBlock = module.block( 'obj_prop_type_default', [targetValueIsAnyBlock].concat( module.call( structdyn.StructDyn.struct_set_indirect_anyref, [ module.local.get(objRef_Idx, binaryen.anyref), module.local.get(index_Idx, binaryen.i32), FunctionalFuncs.unboxAnyToBase( module, module.local.get(targetValueRef_Idx, binaryen.anyref), ValueTypeKind.STRING, ), ], binaryen.none, ), module.br('obj_prop_type_break'), ), ); targetValueIsAnyBlock = module.block( 'obj_prop_type_break', [targetValueIsAnyBlock].concat( module.call( structdyn.StructDyn.struct_set_indirect_anyref, [ module.local.get(objRef_Idx, binaryen.anyref), module.local.get(index_Idx, binaryen.i32), FunctionalFuncs.unboxAnyToExtrefWithoutCast( module, module.local.get(targetValueRef_Idx, binaryen.anyref), ), ], binaryen.none, ), module.br('obj_prop_type_break'), ), ); const ifTargetValueIsAnyTrue = targetValueIsAnyBlock; const ifPropertyExistTrue = module.if( ifTargetValueIsAny, ifTargetValueIsAnyTrue, module.if( ifObjPropIsAny, module.call( structdyn.StructDyn.struct_set_indirect_anyref, [ module.local.get(objRef_Idx, binaryen.anyref), module.local.get(index_Idx, binaryen.i32), module.local.get(targetValueRef_Idx, binaryen.anyref), ], binaryen.none, ), module.unreachable(), ), ); stmts.push( module.if(ifPropertyUnExist, module.unreachable(), ifPropertyExistTrue), ); return module.block(null, stmts, binaryen.anyref); } function arrayBufferConstructor(module: binaryen.Module) { /* params */ const context_Idx = 0; const this_Idx = 1; const byteLength_Idx = 2; const stmts: binaryen.ExpressionRef[] = []; const i8Array = binaryenCAPI._BinaryenArrayNew( module.ptr, i8ArrayTypeInfo.heapTypeRef, module.local.get(byteLength_Idx, binaryen.i32), module.i32.const(0), ); const arrayBufferStruct = binaryenCAPI._BinaryenStructNew( module.ptr, arrayToPtr([i8Array, module.local.get(byteLength_Idx, binaryen.i32)]) .ptr, 2, arrayBufferTypeInfo.heapTypeRef, ); stmts.push(module.return(arrayBufferStruct)); return module.block(null, stmts); } function dataViewConstructor(module: binaryen.Module) { /* params */ const context_Idx = 0; const this_Idx = 1; const buffer_Idx = 2; const byteOffset_Idx = 3; const byteLength_Idx = 4; /* vars */ /* workaround: in type.d.ts, type is number, not wasmType i32 */ const byteOffset_i32Idx = 5; const byteLength_i32Idx = 6; const stmts: binaryen.ExpressionRef[] = []; const byteOffset_anyref = module.local.get(byteOffset_Idx, binaryen.anyref); const byteLength_anyref = module.local.get(byteLength_Idx, binaryen.anyref); const byteLengthInArrayBuffer_i32ref = binaryenCAPI._BinaryenStructGet( module.ptr, 1, module.local.get(buffer_Idx, arrayBufferTypeInfo.typeRef), arrayBufferTypeInfo.typeRef, false, ); const unboxAnyToI32 = ( module: binaryen.Module, anyref: binaryen.ExpressionRef, valueIdx: number, defaultValue: binaryen.ExpressionRef, ) => { const isUndefined = FunctionalFuncs.isBaseType( module, anyref, dyntype.dyntype_is_undefined, ); const value = FunctionalFuncs.unboxAnyToBase( module, anyref, ValueTypeKind.INT, ); return module.if( isUndefined, module.local.set(valueIdx, defaultValue), module.local.set(valueIdx, value), ); }; stmts.push( unboxAnyToI32( module, byteOffset_anyref, byteOffset_i32Idx, module.i32.const(0), ), ); stmts.push( unboxAnyToI32( module, byteLength_anyref, byteLength_i32Idx, module.i32.sub( byteLengthInArrayBuffer_i32ref, module.local.get(byteOffset_i32Idx, binaryen.i32), ), ), ); const dataViewStruct = binaryenCAPI._BinaryenStructNew( module.ptr, arrayToPtr([ module.local.get(buffer_Idx, arrayBufferTypeInfo.typeRef), module.local.get(byteLength_i32Idx, binaryen.i32), module.local.get(byteOffset_i32Idx, binaryen.i32), ]).ptr, 3, dataViewTypeInfo.heapTypeRef, ); stmts.push(module.return(dataViewStruct)); return module.block(null, stmts); } function dataView_basicOps( module: binaryen.Module, this_idx: number, byteOffset_idx: number, byteOffset_i32_idx: number, this_casted_idx: number, buffer_idx: number, dataViewLength_idx: number, dataViewOffset_idx: number, targetOffset_idx: number, i8Array_idx: number, bytesPreElem: number, littleEndian_idx?: number, littleEndian_i32_idx?: number, ) { const stmts: binaryen.ExpressionRef[] = []; stmts.push( module.local.set( byteOffset_i32_idx, FunctionalFuncs.convertTypeToI32( module, module.local.get(byteOffset_idx, binaryen.f64), ), ), ); stmts.push( module.local.set( this_casted_idx, binaryenCAPI._BinaryenRefCast( module.ptr, module.local.get(this_idx, emptyStructType.typeRef), dataViewTypeInfo.typeRef, ), ), ); stmts.push( module.local.set( buffer_idx, binaryenCAPI._BinaryenStructGet( module.ptr, 0, module.local.get(this_casted_idx, dataViewTypeInfo.typeRef), dataViewTypeInfo.typeRef, false, ), ), ); stmts.push( module.local.set( dataViewLength_idx, binaryenCAPI._BinaryenStructGet( module.ptr, 1, module.local.get(this_casted_idx, dataViewTypeInfo.typeRef), dataViewTypeInfo.typeRef, false, ), ), ); stmts.push( module.local.set( dataViewOffset_idx, binaryenCAPI._BinaryenStructGet( module.ptr, 2, module.local.get(this_casted_idx, dataViewTypeInfo.typeRef), dataViewTypeInfo.typeRef, false, ), ), ); stmts.push( module.local.set( targetOffset_idx, module.i32.add( module.local.get(byteOffset_i32_idx, binaryen.i32), module.local.get(dataViewOffset_idx, binaryen.i32), ), ), ); // if byteOffset_i32_Idx + bytesPreElem > dataViewLength_idx, unreachable stmts.push( module.if( module.i32.gt_s( module.i32.add( module.local.get(byteOffset_i32_idx, binaryen.i32), module.i32.const(bytesPreElem), ), module.local.get(dataViewLength_idx, binaryen.i32), ), module.unreachable(), ), ); // if targetOffset_idx >= arrayBuffer length, unreachable stmts.push( module.if( module.i32.ge_s( module.local.get(targetOffset_idx, binaryen.i32), binaryenCAPI._BinaryenStructGet( module.ptr, 1, module.local.get(buffer_idx, arrayBufferTypeInfo.typeRef), arrayBufferTypeInfo.typeRef, false, ), ), module.unreachable(), ), ); stmts.push( module.local.set( i8Array_idx, binaryenCAPI._BinaryenStructGet( module.ptr, 0, module.local.get(buffer_idx, arrayBufferTypeInfo.typeRef), arrayBufferTypeInfo.typeRef, false, ), ), ); if (littleEndian_idx && littleEndian_i32_idx) { const unboxAnyToBool = ( module: binaryen.Module, anyref: binaryen.ExpressionRef, valueIdx: number, defaultValue: binaryen.ExpressionRef, ) => { const isUndefined = FunctionalFuncs.isBaseType( module, anyref, dyntype.dyntype_is_undefined, ); const value = module.call( dyntype.dyntype_to_bool, [FunctionalFuncs.getDynContextRef(module), anyref], dyntype.bool, ); return module.if( isUndefined, module.local.set(valueIdx, defaultValue), module.local.set(valueIdx, value), ); }; stmts.push( unboxAnyToBool( module, module.local.get(littleEndian_idx, binaryen.anyref), littleEndian_i32_idx, module.i32.const(0), ), ); } return stmts; } function load_i8( module: binaryen.Module, i8Array_idx: number, targetOffset_idx: number, memoryPtrOffset: number, targetPtrOffset: number, ) { return binaryenCAPI._BinaryenArraySet( module.ptr, module.local.get(i8Array_idx, i8ArrayTypeInfo.typeRef), module.i32.add( module.local.get(targetOffset_idx, binaryen.i32), module.i32.const(targetPtrOffset), ), module.i32.load8_s( 0, 1, module.i32.add( module.i32.const(BuiltinNames.memoryReserveOffset), module.i32.const(memoryPtrOffset), ), ), ); } function convertI32ToUnsignedValue( module: binaryen.Module, value_idx: number, value_i32_idx: number, bytesPreElem: number, ) { let mask = 255; switch (bytesPreElem) { case 1: { // 0xFF mask = 255; break; } case 2: { // 0xFFFF mask = 65535; break; } case 4: { // 0xFFFFFFFF mask = 4294967295; break; } } return module.local.set( value_i32_idx, /* convert all value to unsigned value */ module.i32.and( FunctionalFuncs.convertTypeToI32( module, module.local.get(value_idx, binaryen.f64), ), module.i32.const(mask), ), ); } function dataView_setInt8(module: binaryen.Module) { /* params */ const context_idx = 0; const this_idx = 1; const byteOffset_idx = 2; const value_idx = 3; /* vars */ /* workaround: in type.d.ts, type is number, not wasmType i32 */ const byteOffset_i32_idx = 4; const this_casted_idx = 5; const buffer_idx = 6; const dataViewLength_idx = 7; const dataViewOffset_idx = 8; const targetOffset_idx = 9; const value_i32_idx = 10; const i8Array_idx = 11; const stmts: binaryen.ExpressionRef[] = dataView_basicOps( module, this_idx, byteOffset_idx, byteOffset_i32_idx, this_casted_idx, buffer_idx, dataViewLength_idx, dataViewOffset_idx, targetOffset_idx, i8Array_idx, 1, ); stmts.push(convertI32ToUnsignedValue(module, value_idx, value_i32_idx, 1)); stmts.push( binaryenCAPI._BinaryenArraySet( module.ptr, module.local.get(i8Array_idx, i8ArrayTypeInfo.typeRef), module.local.get(targetOffset_idx, binaryen.i32), module.local.get(value_i32_idx, binaryen.i32), ), ); return module.block(null, stmts); } function dataView_setInt16(module: binaryen.Module) { /* params */ const context_idx = 0; const this_idx = 1; const byteOffset_idx = 2; const value_idx = 3; const littleEndian_idx = 4; /* vars */ /* workaround: in type.d.ts, type is number, not wasmType i32 */ const byteOffset_i32_idx = 5; const this_casted_idx = 6; const buffer_idx = 7; const dataViewLength_idx = 8; const dataViewOffset_idx = 9; const targetOffset_idx = 10; const value_i32_idx = 11; const i8Array_idx = 12; const littleEndian_i32_idx = 13; const stmts: binaryen.ExpressionRef[] = dataView_basicOps( module, this_idx, byteOffset_idx, byteOffset_i32_idx, this_casted_idx, buffer_idx, dataViewLength_idx, dataViewOffset_idx, targetOffset_idx, i8Array_idx, 2, littleEndian_idx, littleEndian_i32_idx, ); stmts.push(convertI32ToUnsignedValue(module, value_idx, value_i32_idx, 2)); /* Put elem in linear memory based on if littleEndian */ stmts.push( module.i32.store16( 0, 2, module.i32.const(BuiltinNames.memoryReserveOffset), module.local.get(value_i32_idx, binaryen.i32), ), ); stmts.push( module.if( module.i32.eq( module.local.get(littleEndian_i32_idx, binaryen.i32), module.i32.const(1), ), module.block(null, [ load_i8(module, i8Array_idx, targetOffset_idx, 0, 0), load_i8(module, i8Array_idx, targetOffset_idx, 1, 1), ]), module.block(null, [ load_i8(module, i8Array_idx, targetOffset_idx, 1, 0), load_i8(module, i8Array_idx, targetOffset_idx, 0, 1), ]), ), ); return module.block(null, stmts); } function dataView_setInt32(module: binaryen.Module) { /* params */ const context_idx = 0; const this_idx = 1; const byteOffset_idx = 2; const value_idx = 3; const littleEndian_idx = 4; /* vars */ /* workaround: in type.d.ts, type is number, not wasmType i32 */ const byteOffset_i32_idx = 5; const this_casted_idx = 6; const buffer_idx = 7; const dataViewLength_idx = 8; const dataViewOffset_idx = 9; const targetOffset_idx = 10; const value_i32_idx = 11; const i8Array_idx = 12; const littleEndian_i32_idx = 13; const stmts: binaryen.ExpressionRef[] = dataView_basicOps( module, this_idx, byteOffset_idx, byteOffset_i32_idx, this_casted_idx, buffer_idx, dataViewLength_idx, dataViewOffset_idx, targetOffset_idx, i8Array_idx, 4, littleEndian_idx, littleEndian_i32_idx, ); stmts.push(convertI32ToUnsignedValue(module, value_idx, value_i32_idx, 4)); /* Put elem in linear memory based on if littleEndian */ stmts.push( module.i32.store( 0, 4, module.i32.const(BuiltinNames.memoryReserveOffset), module.local.get(value_i32_idx, binaryen.i32), ), ); stmts.push( module.if( module.i32.eq( module.local.get(littleEndian_i32_idx, binaryen.i32), module.i32.const(1), ), module.block(null, [ load_i8(module, i8Array_idx, targetOffset_idx, 0, 0), load_i8(module, i8Array_idx, targetOffset_idx, 1, 1), load_i8(module, i8Array_idx, targetOffset_idx, 2, 2), load_i8(module, i8Array_idx, targetOffset_idx, 3, 3), ]), module.block(null, [ load_i8(module, i8Array_idx, targetOffset_idx, 3, 0), load_i8(module, i8Array_idx, targetOffset_idx, 2, 1), load_i8(module, i8Array_idx, targetOffset_idx, 1, 2), load_i8(module, i8Array_idx, targetOffset_idx, 0, 3), ]), ), ); return module.block(null, stmts); } function dataView_setFloat32(module: binaryen.Module) { /* params */ const context_idx = 0; const this_idx = 1; const byteOffset_idx = 2; const value_idx = 3; const littleEndian_idx = 4; /* vars */ /* workaround: in type.d.ts, type is number, not wasmType i32 and f32 */ const byteOffset_i32_idx = 5; const this_casted_idx = 6; const buffer_idx = 7; const dataViewLength_idx = 8; const dataViewOffset_idx = 9; const targetOffset_idx = 10; const value_i32_idx = 11; const i8Array_idx = 12; const littleEndian_i32_idx = 13; const stmts: binaryen.ExpressionRef[] = dataView_basicOps( module, this_idx, byteOffset_idx, byteOffset_i32_idx, this_casted_idx, buffer_idx, dataViewLength_idx, dataViewOffset_idx, targetOffset_idx, i8Array_idx, 4, littleEndian_idx, littleEndian_i32_idx, ); /* get i32 value */ stmts.push( module.local.set( value_i32_idx, module.i32.reinterpret( module.f32.demote(module.local.get(value_idx, binaryen.f64)), ), ), ); /* Put elem in linear memory based on if littleEndian */ stmts.push( module.i32.store( 0, 4, module.i32.const(BuiltinNames.memoryReserveOffset), module.local.get(value_i32_idx, binaryen.i32), ), ); stmts.push( module.if( module.i32.eq( module.local.get(littleEndian_i32_idx, binaryen.i32), module.i32.const(1), ), module.block(null, [ load_i8(module, i8Array_idx, targetOffset_idx, 0, 0), load_i8(module, i8Array_idx, targetOffset_idx, 1, 1), load_i8(module, i8Array_idx, targetOffset_idx, 2, 2), load_i8(module, i8Array_idx, targetOffset_idx, 3, 3), ]), module.block(null, [ load_i8(module, i8Array_idx, targetOffset_idx, 3, 0), load_i8(module, i8Array_idx, targetOffset_idx, 2, 1), load_i8(module, i8Array_idx, targetOffset_idx, 1, 2), load_i8(module, i8Array_idx, targetOffset_idx, 0, 3), ]), ), ); return module.block(null, stmts); } function dataView_setFloat64(module: binaryen.Module) { /* params */ const context_idx = 0; const this_idx = 1; const byteOffset_idx = 2; const value_idx = 3; const littleEndian_idx = 4; /* vars */ /* workaround: in type.d.ts, type is number, not wasmType i32 and f32 */ const byteOffset_i32_idx = 5; const this_casted_idx = 6; const buffer_idx = 7; const dataViewLength_idx = 8; const dataViewOffset_idx = 9; const targetOffset_idx = 10; const value_i64_idx = 11; const i8Array_idx = 12; const littleEndian_i32_idx = 13; const stmts: binaryen.ExpressionRef[] = dataView_basicOps( module, this_idx, byteOffset_idx, byteOffset_i32_idx, this_casted_idx, buffer_idx, dataViewLength_idx, dataViewOffset_idx, targetOffset_idx, i8Array_idx, 8, littleEndian_idx, littleEndian_i32_idx, ); /* get i64 value */ stmts.push( module.local.set( value_i64_idx, module.i64.reinterpret(module.local.get(value_idx, binaryen.f64)), ), ); /* Put elem in linear memory based on if littleEndian */ stmts.push( module.i64.store( 0, 8, module.i32.const(BuiltinNames.memoryReserveOffset), module.local.get(value_i64_idx, binaryen.i64), ), ); stmts.push( module.if( module.i32.eq( module.local.get(littleEndian_i32_idx, binaryen.i32), module.i32.const(1), ), module.block(null, [ load_i8(module, i8Array_idx, targetOffset_idx, 0, 0), load_i8(module, i8Array_idx, targetOffset_idx, 1, 1), load_i8(module, i8Array_idx, targetOffset_idx, 2, 2), load_i8(module, i8Array_idx, targetOffset_idx, 3, 3), load_i8(module, i8Array_idx, targetOffset_idx, 4, 4), load_i8(module, i8Array_idx, targetOffset_idx, 5, 5), load_i8(module, i8Array_idx, targetOffset_idx, 6, 6), load_i8(module, i8Array_idx, targetOffset_idx, 7, 7), ]), module.block(null, [ load_i8(module, i8Array_idx, targetOffset_idx, 7, 0), load_i8(module, i8Array_idx, targetOffset_idx, 6, 1), load_i8(module, i8Array_idx, targetOffset_idx, 5, 2), load_i8(module, i8Array_idx, targetOffset_idx, 4, 3), load_i8(module, i8Array_idx, targetOffset_idx, 3, 4), load_i8(module, i8Array_idx, targetOffset_idx, 2, 5), load_i8(module, i8Array_idx, targetOffset_idx, 1, 6), load_i8(module, i8Array_idx, targetOffset_idx, 0, 7), ]), ), ); return module.block(null, stmts); } function store_i8( module: binaryen.Module, i8Array_idx: number, targetOffset_idx: number, memoryPtrOffset: number, targetPtrOffset: number, ) { return module.i32.store8( 0, 1, module.i32.add( module.i32.const(BuiltinNames.memoryReserveOffset), module.i32.const(memoryPtrOffset), ), binaryenCAPI._BinaryenArrayGet( module.ptr, module.local.get(i8Array_idx, i8ArrayTypeInfo.typeRef), module.i32.add( module.local.get(targetOffset_idx, binaryen.i32), module.i32.const(targetPtrOffset), ), i8ArrayTypeInfo.typeRef, false, ), ); } function convertI32ToSignedValue( module: binaryen.Module, res_i32_idx: number, bytesPreElem: number, ) { let conditionMask = 128; let convertMask = -256; switch (bytesPreElem) { case 1: { // 0x80 conditionMask = 128; // 0xFFFFFF00 convertMask = -256; break; } case 2: { // 0x8000 conditionMask = 32768; // 0xFFFF0000 convertMask = -65536; break; } case 4: { // 0x80000000 conditionMask = -2147483648; // 0x00000000 convertMask = 0; break; } } /* convert all value to signed value */ return module.select( module.i32.and( module.local.get(res_i32_idx, binaryen.i32), module.i32.const(conditionMask), ), module.i32.or( module.local.get(res_i32_idx, binaryen.i32), module.i32.const(convertMask), ), module.local.get(res_i32_idx, binaryen.i32), ); } function dataView_getInt8(module: binaryen.Module, isSigned: boolean) { /* params */ const context_idx = 0; const this_idx = 1; const byteOffset_idx = 2; /* vars */ /* workaround: in type.d.ts, type is number, not wasmType i32 */ const byteOffset_i32_idx = 3; const this_casted_idx = 4; const buffer_idx = 5; const dataViewLength_idx = 6; const dataViewOffset_idx = 7; const targetOffset_idx = 8; const i8Array_idx = 9; const res_i32_idx = 10; const stmts: binaryen.ExpressionRef[] = dataView_basicOps( module, this_idx, byteOffset_idx, byteOffset_i32_idx, this_casted_idx, buffer_idx, dataViewLength_idx, dataViewOffset_idx, targetOffset_idx, i8Array_idx, 1, ); stmts.push( module.local.set( res_i32_idx, binaryenCAPI._BinaryenArrayGet( module.ptr, module.local.get(i8Array_idx, i8ArrayTypeInfo.typeRef), module.local.get(targetOffset_idx, binaryen.i32), i8ArrayTypeInfo.typeRef, false, ), ), ); stmts.push( module.return( /* workaround: in type.d.ts, type is number, not wasmType i32 */ isSigned ? module.f64.convert_s.i32( convertI32ToSignedValue(module, res_i32_idx, 1), ) : module.f64.convert_u.i32( module.local.get(res_i32_idx, binaryen.i32), ), ), ); return module.block(null, stmts); } function dataView_getInt16(module: binaryen.Module, isSigned: boolean) { /* params */ const context_idx = 0; const this_idx = 1; const byteOffset_idx = 2; const littleEndian_idx = 3; /* vars */ /* workaround: in type.d.ts, type is number, not wasmType i32 */ const byteOffset_i32_idx = 4; const this_casted_idx = 5; const buffer_idx = 6; const dataViewLength_idx = 7; const dataViewOffset_idx = 8; const targetOffset_idx = 9; const i8Array_idx = 10; const littleEndian_i32_idx = 11; const res_i32_idx = 12; const stmts: binaryen.ExpressionRef[] = dataView_basicOps( module, this_idx, byteOffset_idx, byteOffset_i32_idx, this_casted_idx, buffer_idx, dataViewLength_idx, dataViewOffset_idx, targetOffset_idx, i8Array_idx, 2, littleEndian_idx, littleEndian_i32_idx, ); /* Put elem in linear memory based on if littleEndian */ stmts.push( module.if( module.i32.eq( module.local.get(littleEndian_i32_idx, binaryen.i32), module.i32.const(1), ), module.block(null, [ store_i8(module, i8Array_idx, targetOffset_idx, 0, 0), store_i8(module, i8Array_idx, targetOffset_idx, 1, 1), ]), module.block(null, [ store_i8(module, i8Array_idx, targetOffset_idx, 0, 1), store_i8(module, i8Array_idx, targetOffset_idx, 1, 0), ]), ), ); stmts.push( module.local.set( res_i32_idx, isSigned ? module.i32.load16_s( 0, 2, module.i32.const(BuiltinNames.memoryReserveOffset), ) : module.i32.load16_u( 0, 2, module.i32.const(BuiltinNames.memoryReserveOffset), ), ), ); stmts.push( module.return( /* workaround: in type.d.ts, type is number, not wasmType i32 */ isSigned ? module.f64.convert_s.i32( convertI32ToSignedValue(module, res_i32_idx, 2), ) : module.f64.convert_u.i32( module.local.get(res_i32_idx, binaryen.i32), ), ), ); return module.block(null, stmts); } function dataView_getInt32(module: binaryen.Module, isSigned: boolean) { /* params */ const context_idx = 0; const this_idx = 1; const byteOffset_idx = 2; const littleEndian_idx = 3; /* vars */ /* workaround: in type.d.ts, type is number, not wasmType i32 */ const byteOffset_i32_idx = 4; const this_casted_idx = 5; const buffer_idx = 6; const dataViewLength_idx = 7; const dataViewOffset_idx = 8; const targetOffset_idx = 9; const i8Array_idx = 10; const littleEndian_i32_idx = 11; const res_i32_idx = 12; const stmts: binaryen.ExpressionRef[] = dataView_basicOps( module, this_idx, byteOffset_idx, byteOffset_i32_idx, this_casted_idx, buffer_idx, dataViewLength_idx, dataViewOffset_idx, targetOffset_idx, i8Array_idx, 4, littleEndian_idx, littleEndian_i32_idx, ); /* Put elem in linear memory based on if littleEndian */ stmts.push( module.if( module.i32.eq( module.local.get(littleEndian_i32_idx, binaryen.i32), module.i32.const(1), ), module.block(null, [ store_i8(module, i8Array_idx, targetOffset_idx, 0, 0), store_i8(module, i8Array_idx, targetOffset_idx, 1, 1), store_i8(module, i8Array_idx, targetOffset_idx, 2, 2), store_i8(module, i8Array_idx, targetOffset_idx, 3, 3), ]), module.block(null, [ store_i8(module, i8Array_idx, targetOffset_idx, 0, 3), store_i8(module, i8Array_idx, targetOffset_idx, 1, 2), store_i8(module, i8Array_idx, targetOffset_idx, 2, 1), store_i8(module, i8Array_idx, targetOffset_idx, 3, 0), ]), ), ); stmts.push( module.local.set( res_i32_idx, module.i32.load( 0, 4, module.i32.const(BuiltinNames.memoryReserveOffset), ), ), ); stmts.push( module.return( /* workaround: in type.d.ts, type is number, not wasmType i32 */ isSigned ? module.f64.convert_s.i32( convertI32ToSignedValue(module, res_i32_idx, 4), ) : module.f64.convert_u.i32( module.local.get(res_i32_idx, binaryen.i32), ), ), ); return module.block(null, stmts); } function dataView_getFloat32(module: binaryen.Module) { /* params */ const context_idx = 0; const this_idx = 1; const byteOffset_idx = 2; const littleEndian_idx = 3; /* vars */ /* workaround: in type.d.ts, type is number, not wasmType i32 */ const byteOffset_i32_idx = 4; const this_casted_idx = 5; const buffer_idx = 6; const dataViewLength_idx = 7; const dataViewOffset_idx = 8; const targetOffset_idx = 9; const i8Array_idx = 10; const littleEndian_i32_idx = 11; const res_i32_idx = 12; const stmts: binaryen.ExpressionRef[] = dataView_basicOps( module, this_idx, byteOffset_idx, byteOffset_i32_idx, this_casted_idx, buffer_idx, dataViewLength_idx, dataViewOffset_idx, targetOffset_idx, i8Array_idx, 4, littleEndian_idx, littleEndian_i32_idx, ); /* Put elem in linear memory based on if littleEndian */ stmts.push( module.if( module.i32.eq( module.local.get(littleEndian_i32_idx, binaryen.i32), module.i32.const(1), ), module.block(null, [ store_i8(module, i8Array_idx, targetOffset_idx, 0, 0), store_i8(module, i8Array_idx, targetOffset_idx, 1, 1), store_i8(module, i8Array_idx, targetOffset_idx, 2, 2), store_i8(module, i8Array_idx, targetOffset_idx, 3, 3), ]), module.block(null, [ store_i8(module, i8Array_idx, targetOffset_idx, 0, 3), store_i8(module, i8Array_idx, targetOffset_idx, 1, 2), store_i8(module, i8Array_idx, targetOffset_idx, 2, 1), store_i8(module, i8Array_idx, targetOffset_idx, 3, 0), ]), ), ); stmts.push( module.local.set( res_i32_idx, module.i32.load( 0, 4, module.i32.const(BuiltinNames.memoryReserveOffset), ), ), ); stmts.push( module.return( /* workaround: in type.d.ts, type is number, not wasmType f32 */ module.f64.promote( module.f32.reinterpret( module.local.get(res_i32_idx, binaryen.i32), ), ), ), ); return module.block(null, stmts); } function dataView_getFloat64(module: binaryen.Module) { /* params */ const context_idx = 0; const this_idx = 1; const byteOffset_idx = 2; const littleEndian_idx = 3; /* vars */ /* workaround: in type.d.ts, type is number, not wasmType i32 */ const byteOffset_i32_idx = 4; const this_casted_idx = 5; const buffer_idx = 6; const dataViewLength_idx = 7; const dataViewOffset_idx = 8; const targetOffset_idx = 9; const i8Array_idx = 10; const littleEndian_i32_idx = 11; const res_i64_idx = 12; const stmts: binaryen.ExpressionRef[] = dataView_basicOps( module, this_idx, byteOffset_idx, byteOffset_i32_idx, this_casted_idx, buffer_idx, dataViewLength_idx, dataViewOffset_idx, targetOffset_idx, i8Array_idx, 8, littleEndian_idx, littleEndian_i32_idx, ); /* Put elem in linear memory based on if littleEndian */ stmts.push( module.if( module.i32.eq( module.local.get(littleEndian_i32_idx, binaryen.i32), module.i32.const(1), ), module.block(null, [ store_i8(module, i8Array_idx, targetOffset_idx, 0, 0), store_i8(module, i8Array_idx, targetOffset_idx, 1, 1), store_i8(module, i8Array_idx, targetOffset_idx, 2, 2), store_i8(module, i8Array_idx, targetOffset_idx, 3, 3), store_i8(module, i8Array_idx, targetOffset_idx, 4, 4), store_i8(module, i8Array_idx, targetOffset_idx, 5, 5), store_i8(module, i8Array_idx, targetOffset_idx, 6, 6), store_i8(module, i8Array_idx, targetOffset_idx, 7, 7), ]), module.block(null, [ store_i8(module, i8Array_idx, targetOffset_idx, 0, 7), store_i8(module, i8Array_idx, targetOffset_idx, 1, 6), store_i8(module, i8Array_idx, targetOffset_idx, 2, 5), store_i8(module, i8Array_idx, targetOffset_idx, 3, 4), store_i8(module, i8Array_idx, targetOffset_idx, 4, 3), store_i8(module, i8Array_idx, targetOffset_idx, 5, 2), store_i8(module, i8Array_idx, targetOffset_idx, 6, 1), store_i8(module, i8Array_idx, targetOffset_idx, 7, 0), ]), ), ); stmts.push( module.local.set( res_i64_idx, module.i64.load( 0, 8, module.i32.const(BuiltinNames.memoryReserveOffset), ), ), ); stmts.push( module.return( /* workaround: in type.d.ts, type is number, not wasmType f32 */ module.f64.reinterpret(module.local.get(res_i64_idx, binaryen.i64)), ), ); return module.block(null, stmts); } function string_fromCharCode(module: binaryen.Module) { /* params */ const context_idx = 0; const this_idx = 1; const codes_idx = 2; /* vars */ const loopIdx_i32_idx = 3; const codesLen_i32_idx = 4; const stmts: binaryen.ExpressionRef[] = []; stmts.push(module.local.set(loopIdx_i32_idx, module.i32.const(0))); const loopIndexValue = module.local.get(loopIdx_i32_idx, binaryen.i32); const codesArray = binaryenCAPI._BinaryenStructGet( module.ptr, 0, module.local.get(codes_idx, numberArrayStructTypeInfo.typeRef), numberArrayStructTypeInfo.typeRef, false, ); const codeLen = binaryenCAPI._BinaryenStructGet( module.ptr, 1, module.local.get(codes_idx, numberArrayStructTypeInfo.typeRef), numberArrayStructTypeInfo.typeRef, false, ); stmts.push(module.local.set(codesLen_i32_idx, codeLen)); /* Put elem in linear memory */ const loopLabel = 'for_label'; const loopCond = module.i32.lt_s( loopIndexValue, module.local.get(codesLen_i32_idx, binaryen.i32), ); const loopIncrementor = module.local.set( loopIdx_i32_idx, module.i32.add(loopIndexValue, module.i32.const(1)), ); const loopBody: binaryen.ExpressionRef[] = []; loopBody.push( module.i32.store( 0, 4, module.i32.add( module.i32.const(BuiltinNames.memoryReserveOffset), loopIndexValue, ), FunctionalFuncs.convertTypeToI32( module, binaryenCAPI._BinaryenArrayGet( module.ptr, codesArray, loopIndexValue, numberArrayTypeInfo.typeRef, false, ), ), ), ); const flattenLoop: FlattenLoop = { label: loopLabel, condition: loopCond, statements: module.block(null, loopBody), incrementor: loopIncrementor, }; stmts.push( module.loop( loopLabel, FunctionalFuncs.flattenLoopStatement( module, flattenLoop, SemanticsKind.FOR, ), ), ); stmts.push( module.return( FunctionalFuncs.generateStringForStringref( module, module.i32.const(BuiltinNames.memoryReserveOffset), module.local.get(codesLen_i32_idx, binaryen.i32), ), ), ); return module.block(null, stmts); } function generateSwitchBlock( module: binaryen.Module, typeIdRefValue: binaryen.ExpressionRef, anyTypedRes_Idx: number, ownerRefValue: binaryen.ExpressionRef, idxI32RefValue: binaryen.ExpressionRef, ) { const branches: binaryen.ExpressionRef[] = new Array(8); branches[0] = module.br( 'case_field_type_is_number', FunctionalFuncs.isPropTypeIdEqual( module, typeIdRefValue, module.i32.const(PredefinedTypeId.NUMBER), ), ); branches[1] = module.br( 'case_field_type_is_i32', FunctionalFuncs.isPropTypeIdEqual( module, typeIdRefValue, module.i32.const(PredefinedTypeId.INT), ), ); branches[2] = module.br( 'case_field_type_is_i64', FunctionalFuncs.isPropTypeIdEqual( module, typeIdRefValue, module.i32.const(PredefinedTypeId.WASM_I64), ), ); branches[3] = module.br( 'case_field_type_is_f32', FunctionalFuncs.isPropTypeIdEqual( module, typeIdRefValue, module.i32.const(PredefinedTypeId.WASM_F32), ), ); branches[4] = module.br( 'case_field_type_is_boolean', FunctionalFuncs.isPropTypeIdEqual( module, typeIdRefValue, module.i32.const(PredefinedTypeId.BOOLEAN), ), ); branches[5] = module.br( 'case_field_type_is_string', FunctionalFuncs.isPropTypeIdEqual( module, typeIdRefValue, module.i32.const(PredefinedTypeId.STRING), ), ); branches[6] = module.br( 'case_field_type_is_any', FunctionalFuncs.isPropTypeIdEqual( module, typeIdRefValue, module.i32.const(PredefinedTypeId.ANY), ), ); branches[7] = module.br('field_type_default'); let switchBlock = module.block('case_field_type_is_number', branches); switchBlock = module.block( 'case_field_type_is_i32', [switchBlock].concat( module.local.set( anyTypedRes_Idx, FunctionalFuncs.generateDynNumber( module, module.call( structdyn.StructDyn.struct_get_indirect_f64, [ownerRefValue, idxI32RefValue], binaryen.f64, ), ), ), module.br('field_type_break'), ), ); switchBlock = module.block( 'case_field_type_is_i64', [switchBlock].concat( module.local.set( anyTypedRes_Idx, FunctionalFuncs.generateDynNumber( module, module.call( structdyn.StructDyn.struct_get_indirect_i32, [ownerRefValue, idxI32RefValue], binaryen.i32, ), ), ), module.br('field_type_break'), ), ); switchBlock = module.block( 'case_field_type_is_f32', [switchBlock].concat( module.local.set( anyTypedRes_Idx, FunctionalFuncs.generateDynNumber( module, module.call( structdyn.StructDyn.struct_get_indirect_i64, [ownerRefValue, idxI32RefValue], binaryen.i64, ), ), ), module.br('field_type_break'), ), ); switchBlock = module.block( 'case_field_type_is_boolean', [switchBlock].concat( module.local.set( anyTypedRes_Idx, FunctionalFuncs.generateDynNumber( module, module.call( structdyn.StructDyn.struct_get_indirect_f32, [ownerRefValue, idxI32RefValue], binaryen.f32, ), ), ), module.br('field_type_break'), ), ); switchBlock = module.block( 'case_field_type_is_string', [switchBlock].concat( module.local.set( anyTypedRes_Idx, FunctionalFuncs.generateDynBoolean( module, module.call( structdyn.StructDyn.struct_get_indirect_i32, [ownerRefValue, idxI32RefValue], binaryen.i32, ), ), ), module.br('field_type_break'), ), ); switchBlock = module.block( 'case_field_type_is_any', [switchBlock].concat( module.local.set( anyTypedRes_Idx, FunctionalFuncs.generateDynString( module, module.call( structdyn.StructDyn.struct_get_indirect_anyref, [ownerRefValue, idxI32RefValue], binaryen.anyref, ), ), ), module.br('field_type_break'), ), ); switchBlock = module.block( 'field_type_default', [switchBlock].concat( module.local.set( anyTypedRes_Idx, module.call( structdyn.StructDyn.struct_get_indirect_anyref, [ownerRefValue, idxI32RefValue], binaryen.anyref, ), ), module.br('field_type_break'), ), ); switchBlock = module.block( 'field_type_break', [switchBlock].concat( module.local.set( anyTypedRes_Idx, FunctionalFuncs.generateDynExtref( module, module.call( structdyn.StructDyn.struct_get_indirect_anyref, [ownerRefValue, idxI32RefValue], binaryen.anyref, ), FunctionalFuncs.getExtTagRefByTypeIdRef( module, typeIdRefValue, ), ), ), module.br('field_type_break'), ), ); return switchBlock; } function WASMStruct_get_field(module: binaryen.Module) { /* params */ const typeIdArray_idx = 0; const idxI32Ref_idx = 1; const ownerRef_idx = 2; /* vars */ const arrLen_i32_idx = 3; const loopIdx_i32_idx = 4; const typeIdRef_idx = 5; const anyTypedRes_Idx = 6; const stmts: binaryen.ExpressionRef[] = []; stmts.push(module.local.set(loopIdx_i32_idx, module.i32.const(0))); const typeIdArrayValue = module.local.get( typeIdArray_idx, i32ArrayTypeInfo.typeRef, ); const loopIndexValue = module.local.get(loopIdx_i32_idx, binaryen.i32); const idxI32RefValue = module.local.get(idxI32Ref_idx, binaryen.i32); const typeIdRefValue = module.local.get(typeIdRef_idx, binaryen.i32); const ownerRefValue = module.local.get(ownerRef_idx, binaryen.anyref); const arrLen = binaryenCAPI._BinaryenArrayLen(module.ptr, typeIdArrayValue); stmts.push(module.local.set(arrLen_i32_idx, arrLen)); /* get the field typeId through for loop */ const loopLabel = 'for_label'; const loopCond = module.i32.lt_s( loopIndexValue, module.local.get(arrLen_i32_idx, binaryen.i32), ); const loopIncrementor = module.local.set( loopIdx_i32_idx, module.i32.add(loopIndexValue, module.i32.const(1)), ); const loopBody: binaryen.ExpressionRef[] = []; loopBody.push( module.if( module.i32.eq(loopIndexValue, idxI32RefValue), module.block(null, [ module.local.set( typeIdRef_idx, binaryenCAPI._BinaryenArrayGet( module.ptr, typeIdArrayValue, idxI32RefValue, i32ArrayTypeInfo.typeRef, false, ), ), ]), ), ); const flattenLoop: FlattenLoop = { label: loopLabel, condition: loopCond, statements: module.block(null, loopBody), incrementor: loopIncrementor, }; stmts.push( module.loop( loopLabel, FunctionalFuncs.flattenLoopStatement( module, flattenLoop, SemanticsKind.FOR, ), ), ); const switchBlock = generateSwitchBlock( module, typeIdRefValue, anyTypedRes_Idx, ownerRefValue, idxI32RefValue, ); stmts.push(switchBlock); stmts.push( module.return(module.local.get(anyTypedRes_Idx, binaryen.anyref)), ); return module.block(null, stmts); } export function callBuiltInAPIs(module: binaryen.Module) { /** Math.sqrt */ module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.mathSqrtFuncName, ), binaryen.createType([ emptyStructType.typeRef, emptyStructType.typeRef, binaryen.f64, ]), binaryen.f64, [], module.f64.sqrt(module.local.get(2, binaryen.f64)), ); /** Math.abs */ module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.mathAbsFuncName, ), binaryen.createType([ emptyStructType.typeRef, emptyStructType.typeRef, binaryen.f64, ]), binaryen.f64, [], module.f64.abs(module.local.get(2, binaryen.f64)), ); /** Math.ceil */ module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.mathCeilFuncName, ), binaryen.createType([ emptyStructType.typeRef, emptyStructType.typeRef, binaryen.f64, ]), binaryen.f64, [], module.f64.ceil(module.local.get(2, binaryen.f64)), ); /** Math.floor */ module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.mathFloorFuncName, ), binaryen.createType([ emptyStructType.typeRef, emptyStructType.typeRef, binaryen.f64, ]), binaryen.f64, [], module.f64.floor(module.local.get(2, binaryen.f64)), ); /** Math.trunc */ module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.mathTruncFuncName, ), binaryen.createType([ emptyStructType.typeRef, emptyStructType.typeRef, binaryen.f64, ]), binaryen.f64, [], module.f64.trunc(module.local.get(2, binaryen.f64)), ); /** Array.isArray */ module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.arrayIsArrayFuncName, ), binaryen.createType([ emptyStructType.typeRef, emptyStructType.typeRef, binaryen.anyref, ]), binaryen.i32, [binaryen.i32], Array_isArray(module), ); /** anyref */ module.addFunction( getBuiltInFuncName(BuiltinNames.anyrefCond), binaryen.createType([binaryen.anyref]), binaryen.i32, [], anyrefCond(module), ); module.addFunction( getBuiltInFuncName(BuiltinNames.allocExtRefTableSlot), binaryen.createType([binaryen.anyref]), binaryen.i32, [binaryen.i32, binaryen.i32, i8ArrayTypeInfo.typeRef], allocExtRefTableSlot(module), ); module.addFunction( getBuiltInFuncName(BuiltinNames.newExtRef), binaryen.createType([ dyntype.dyn_ctx_t, dyntype.external_ref_tag, binaryen.anyref, ]), binaryen.anyref, [], newExtRef(module), ); module.addFunctionExport( getBuiltInFuncName(BuiltinNames.allocExtRefTableSlot), BuiltinNames.allocExtRefTableSlot, ); module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.getPropertyIfTypeIdMismatch, ), binaryen.createType([ binaryen.i32, binaryen.i32, binaryen.i32, binaryen.anyref, ]), binaryen.anyref, [binaryen.i32, binaryen.i32, binaryen.anyref], getPropertyIfTypeIdMismatch(module), ); module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.setPropertyIfTypeIdMismatch, ), binaryen.createType([ binaryen.i32, binaryen.i32, binaryen.i32, binaryen.anyref, binaryen.anyref, ]), binaryen.none, [binaryen.i32, binaryen.i32], setPropertyIfTypeIdMismatch(module), ); /** string */ if (getConfig().enableStringRef) { module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringConcatFuncName, ), binaryen.createType([ emptyStructType.typeRef, binaryenCAPI._BinaryenTypeStringref(), stringrefArrayStructTypeInfo.typeRef, ]), binaryenCAPI._BinaryenTypeStringref(), [ binaryenCAPI._BinaryenTypeStringref(), binaryen.i32, binaryen.i32, stringrefArrayTypeInfo.typeRef, ], string_concat_stringref(module), ); module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringEQFuncName, ), binaryen.createType([ binaryenCAPI._BinaryenTypeStringref(), binaryenCAPI._BinaryenTypeStringref(), ]), binaryen.i32, [], string_eq_stringref(module), ); module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringSliceFuncName, ), binaryen.createType([ emptyStructType.typeRef, binaryenCAPI._BinaryenTypeStringref(), binaryen.anyref, binaryen.anyref, ]), binaryenCAPI._BinaryenTypeStringref(), [ binaryen.i32, binaryen.i32, binaryen.i32, binaryenCAPI._BinaryenTypeStringviewWTF16(), ], string_slice_stringref(module), ); module.addFunction( getBuiltInFuncName(BuiltinNames.stringcharAtFuncName), binaryen.createType([ emptyStructType.typeRef, binaryenCAPI._BinaryenTypeStringref(), binaryen.f64, ]), binaryenCAPI._BinaryenTypeStringref(), [binaryen.i32, binaryen.i32], string_charAt_stringref(module), ); module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringIndexOfInternalFuncName, ), binaryen.createType([ emptyStructType.typeRef, binaryenCAPI._BinaryenTypeStringref(), binaryenCAPI._BinaryenTypeStringref(), binaryen.i32, binaryen.i32, ]), binaryen.i32, [ binaryen.i32, binaryen.i32, binaryen.i32, binaryenCAPI._BinaryenTypeStringref(), binaryen.i32, binaryen.i32, ], string_indexOf_internal_stringref(module), ); module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringIndexOfFuncName, ), binaryen.createType([ emptyStructType.typeRef, binaryenCAPI._BinaryenTypeStringref(), binaryenCAPI._BinaryenTypeStringref(), ]), binaryen.f64, [], string_indexOf_stringref(module), ); module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringLastIndexOfFuncName, ), binaryen.createType([ emptyStructType.typeRef, binaryenCAPI._BinaryenTypeStringref(), binaryenCAPI._BinaryenTypeStringref(), ]), binaryen.f64, [], string_lastIndexOf_stringref(module), ); module.addFunction( getBuiltInFuncName(BuiltinNames.stringtrimFuncName), binaryen.createType([ emptyStructType.typeRef, binaryenCAPI._BinaryenTypeStringref(), ]), binaryenCAPI._BinaryenTypeStringref(), [binaryen.i32, binaryen.i32, binaryen.i32], string_trim_stringref(module), ); module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringSplitFuncName, ), binaryen.createType([ emptyStructType.typeRef, binaryenCAPI._BinaryenTypeStringref(), binaryenCAPI._BinaryenTypeStringref(), ]), stringrefArrayStructTypeInfo.typeRef, [ stringrefArrayTypeInfo.typeRef, binaryen.i32, binaryen.i32, binaryen.i32, binaryen.i32, binaryenCAPI._BinaryenTypeStringref(), ], string_split_stringref(module), ); module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringMatchFuncName, ), binaryen.createType([ emptyStructType.typeRef, binaryenCAPI._BinaryenTypeStringref(), binaryenCAPI._BinaryenTypeStringref(), ]), stringrefArrayStructTypeInfo.typeRef, [binaryen.i32, stringrefArrayTypeInfo.typeRef], string_match_stringref(module), ); module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringSearchFuncName, ), binaryen.createType([ emptyStructType.typeRef, binaryenCAPI._BinaryenTypeStringref(), binaryenCAPI._BinaryenTypeStringref(), ]), binaryen.f64, [], string_search_stringref(module), ); module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringReplaceFuncName, ), binaryen.createType([ emptyStructType.typeRef, binaryenCAPI._BinaryenTypeStringref(), binaryenCAPI._BinaryenTypeStringref(), binaryenCAPI._BinaryenTypeStringref(), ]), binaryenCAPI._BinaryenTypeStringref(), [binaryen.i32, binaryen.i32], string_replace_stringref(module), ); module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringSubStringFuncName, ), binaryen.createType([ emptyStructType.typeRef, binaryenCAPI._BinaryenTypeStringref(), binaryen.f64, binaryen.anyref, ]), binaryenCAPI._BinaryenTypeStringref(), [binaryen.i32, binaryen.i32, binaryen.i32, binaryen.i32], string_substring_stringref(module), ); module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringCharCodeAtFuncName, ), binaryen.createType([ emptyStructType.typeRef, binaryenCAPI._BinaryenTypeStringref(), binaryen.f64, ]), binaryen.f64, [binaryen.i32, binaryen.i32, binaryen.i32], string_charCodeAt_stringref(module), ); module.addFunction( getBuiltInFuncName(BuiltinNames.stringtoLowerCaseFuncName), binaryen.createType([ emptyStructType.typeRef, binaryenCAPI._BinaryenTypeStringref(), ]), binaryenCAPI._BinaryenTypeStringref(), [], string_toLowerCase_stringref(module), ); module.addFunction( getBuiltInFuncName(BuiltinNames.stringtoUpperCaseFuncName), binaryen.createType([ emptyStructType.typeRef, binaryenCAPI._BinaryenTypeStringref(), ]), binaryenCAPI._BinaryenTypeStringref(), [], string_toUpperCase_stringref(module), ); /** For now, here should enable --enableStringref flag to get prop name * through meta */ module.addFunction( getUtilsFuncName(BuiltinNames.getPropNamesByMeta), baseStructType.typeRef, stringrefArrayStructTypeInfo.typeRef, [ stringrefArrayTypeInfo.typeRef, binaryen.i32, binaryen.i32, binaryen.i32, binaryen.i32, binaryen.i32, binaryen.i32, binaryen.i32, binaryen.i32, binaryen.i32, ], getPropNameThroughMeta(module), ); } else { module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringConcatFuncName, ), binaryen.createType([ emptyStructType.typeRef, stringTypeInfo.typeRef, stringArrayStructTypeInfo.typeRef, ]), stringTypeInfo.typeRef, [binaryen.i32, binaryen.i32, i8ArrayTypeInfo.typeRef, binaryen.i32], string_concat(module), ); module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringEQFuncName, ), binaryen.createType([ stringTypeInfo.typeRef, stringTypeInfo.typeRef, ]), binaryen.i32, [binaryen.i32], string_eq(module), ); module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringSliceFuncName, ), binaryen.createType([ emptyStructType.typeRef, stringTypeInfo.typeRef, binaryen.anyref, binaryen.anyref, ]), stringTypeInfo.typeRef, [binaryen.i32, binaryen.i32, i8ArrayTypeInfo.typeRef], string_slice(module), ); module.addFunction( getBuiltInFuncName(BuiltinNames.stringcharAtFuncName), binaryen.createType([ emptyStructType.typeRef, stringTypeInfo.typeRef, binaryen.f64, ]), stringTypeInfo.typeRef, [i8ArrayTypeInfo.typeRef], string_charAt(module), ); module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringIndexOfFuncName, ), binaryen.createType([ emptyStructType.typeRef, stringTypeInfo.typeRef, stringTypeInfo.typeRef, ]), binaryen.f64, [binaryen.i32, binaryen.i32, binaryen.i32], string_indexOf(module), ); module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringIndexOfInternalFuncName, ), binaryen.createType([ emptyStructType.typeRef, stringTypeInfo.typeRef, stringTypeInfo.typeRef, binaryen.i32, ]), binaryen.i32, [ binaryen.i32, binaryen.i32, binaryen.i32, binaryen.i32, binaryen.i32, ], string_indexOf_internal(module), ); module.addFunction( getBuiltInFuncName(BuiltinNames.stringtrimFuncName), binaryen.createType([ emptyStructType.typeRef, stringTypeInfo.typeRef, ]), stringTypeInfo.typeRef, [ binaryen.i32, i8ArrayTypeInfo.typeRef, binaryen.i32, binaryen.i32, binaryen.i32, binaryen.i32, ], string_trim(module), ); module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringSplitFuncName, ), binaryen.createType([ emptyStructType.typeRef, stringTypeInfo.typeRef, stringTypeInfo.typeRef, ]), stringArrayStructTypeInfo.typeRef, [ binaryen.i32, binaryen.i32, binaryen.i32, binaryen.i32, binaryen.i32, stringArrayTypeInfo.typeRef, binaryen.i32, i8ArrayTypeInfo.typeRef, binaryen.i32, ], string_split(module), ); module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringMatchFuncName, ), binaryen.createType([ emptyStructType.typeRef, stringTypeInfo.typeRef, stringTypeInfo.typeRef, ]), stringArrayStructTypeInfo.typeRef, [ binaryen.i32, stringArrayTypeInfo.typeRef, binaryen.i32, binaryen.i32, i8ArrayTypeInfo.typeRef, binaryen.i32, binaryen.i32, ], string_match(module), ); module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringSearchFuncName, ), binaryen.createType([ emptyStructType.typeRef, stringTypeInfo.typeRef, stringTypeInfo.typeRef, ]), binaryen.f64, [binaryen.i32, binaryen.f64], string_search(module), ); module.addFunction( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringReplaceFuncName, ), binaryen.createType([ emptyStructType.typeRef, stringTypeInfo.typeRef, stringTypeInfo.typeRef, stringTypeInfo.typeRef, ]), stringTypeInfo.typeRef, [i8ArrayTypeInfo.typeRef, binaryen.i32], string_replace(module), ); module.addFunction( getBuiltInFuncName(BuiltinNames.stringtoLowerCaseFuncName), binaryen.createType([ emptyStructType.typeRef, stringTypeInfo.typeRef, ]), stringTypeInfo.typeRef, [binaryen.i32, i8ArrayTypeInfo.typeRef, binaryen.i32, binaryen.i32], string_toLowerCase(module), ); module.addFunction( getBuiltInFuncName(BuiltinNames.stringtoUpperCaseFuncName), binaryen.createType([ emptyStructType.typeRef, stringTypeInfo.typeRef, ]), stringTypeInfo.typeRef, [binaryen.i32, i8ArrayTypeInfo.typeRef, binaryen.i32, binaryen.i32], string_toUpperCase(module), ); } /** array */ /* e.g. array.push can be implemented by a single native API, since the native API doesn't directly receive or return element of the array, there is no need to do specialization for function type */ addArrayMethod( module, 'push', BuiltinNames.arrayPushFuncNames, true, [binaryen.anyref, binaryen.anyref], binaryen.f64, ); /* e.g. array.pop's return type is T, must be implemented through multiple native APIs to handle value types (i32, i64, ...) and ref type (anyref) */ addArrayMethod( module, 'pop', BuiltinNames.arrayPopFuncNames, false, [binaryen.anyref], null, ); addArrayMethod( module, 'join', BuiltinNames.arrayJoinFuncNames, false, [binaryen.anyref, binaryen.anyref], getConfig().enableStringRef ? binaryenCAPI._BinaryenTypeStringref() : stringTypeInfo.typeRef, ); addArrayMethod( module, 'concat', BuiltinNames.arrayConcatFuncNames, true, [binaryen.anyref, binaryen.anyref], binaryen.anyref, ); addArrayMethod( module, 'reverse', BuiltinNames.arrayReverseFuncNames, true, [binaryen.anyref], binaryen.anyref, ); addArrayMethod( module, 'shift', BuiltinNames.arrayShiftFuncNames, false, [binaryen.anyref], null, ); addArrayMethod( module, 'slice', BuiltinNames.arraySliceFuncNames, true, [binaryen.anyref, binaryen.anyref, binaryen.anyref], binaryen.anyref, ); addArrayMethod( module, 'sort', BuiltinNames.arraySortFuncNames, true, [binaryen.anyref, binaryen.anyref], binaryen.anyref, ); addArrayMethod( module, 'splice', BuiltinNames.arraySpliceFuncNames, true, [binaryen.anyref, binaryen.f64, binaryen.anyref, binaryen.anyref], binaryen.anyref, ); addArrayMethod( module, 'unshift', BuiltinNames.arrayUnshiftFuncNames, true, [binaryen.anyref, binaryen.anyref], binaryen.f64, ); addArrayMethod( module, 'indexOf', BuiltinNames.arrayIndexOfFuncNames, false, [binaryen.anyref, null, binaryen.anyref], binaryen.f64, ); addArrayMethod( module, 'lastIndexOf', BuiltinNames.arrayLastIndexOfFuncNames, false, [binaryen.anyref, null, binaryen.anyref], binaryen.f64, ); addArrayMethod( module, 'every', BuiltinNames.arrayEveryFuncNames, true, [binaryen.anyref, binaryen.anyref], binaryen.i32, ); addArrayMethod( module, 'some', BuiltinNames.arraySomeFuncNames, true, [binaryen.anyref, binaryen.anyref], binaryen.i32, ); addArrayMethod( module, 'forEach', BuiltinNames.arrayForEachFuncNames, true, [binaryen.anyref, binaryen.anyref], binaryen.none, ); addArrayMethod( module, 'map', BuiltinNames.arrayMapFuncNames, true, [binaryen.anyref, binaryen.anyref], binaryen.anyref, ); addArrayMethod( module, 'filter', BuiltinNames.arrayFilterFuncNames, true, [binaryen.anyref, binaryen.anyref], binaryen.anyref, ); addArrayMethod( module, 'reduce', BuiltinNames.arrayReduceFuncNames, false, [binaryen.anyref, binaryen.anyref, null], null, ); addArrayMethod( module, 'reduceRight', BuiltinNames.arrayReduceRightFuncNames, false, [binaryen.anyref, binaryen.anyref, null], null, ); addArrayMethod( module, 'find', BuiltinNames.arrayFindFuncNames, true, [binaryen.anyref, binaryen.anyref], binaryen.anyref, ); addArrayMethod( module, 'findIndex', BuiltinNames.arrayFindIndexFuncNames, true, [binaryen.anyref, binaryen.anyref], binaryen.f64, ); addArrayMethod( module, 'fill', BuiltinNames.arrayFillFuncNames, false, [binaryen.anyref, null, binaryen.anyref, binaryen.anyref], binaryen.anyref, ); addArrayMethod( module, 'copyWithin', BuiltinNames.arrayCopyWithinFuncNames, true, [binaryen.anyref, binaryen.f64, binaryen.f64, binaryen.anyref], binaryen.anyref, ); addArrayMethod( module, 'includes', BuiltinNames.arrayIncludesFuncNames, false, [binaryen.anyref, null, binaryen.anyref], binaryen.i32, ); /* builtin class methods */ module.addFunction( UtilFuncs.getBuiltinClassCtorName(BuiltinNames.ARRAYBUFFER), binaryen.createType([ emptyStructType.typeRef, emptyStructType.typeRef, binaryen.i32, ]), arrayBufferTypeInfo.typeRef, [], arrayBufferConstructor(module), ); module.addFunctionImport( UtilFuncs.getBuiltinClassMethodName(BuiltinNames.ARRAYBUFFER, 'slice'), 'env', 'array_slice_generic', binaryen.createType([ binaryen.anyref, binaryen.anyref, binaryen.anyref, binaryen.anyref, ]), binaryen.anyref, ); module.addFunction( UtilFuncs.getBuiltinClassCtorName(BuiltinNames.DATAVIEW), binaryen.createType([ emptyStructType.typeRef, emptyStructType.typeRef, arrayBufferTypeInfo.typeRef, binaryen.anyref, binaryen.anyref, ]), dataViewTypeInfo.typeRef, [binaryen.i32, binaryen.i32], dataViewConstructor(module), ); module.addFunction( UtilFuncs.getBuiltinClassMethodName(BuiltinNames.DATAVIEW, 'setInt8'), binaryen.createType([ emptyStructType.typeRef, emptyStructType.typeRef, binaryen.f64, binaryen.f64, ]), binaryen.none, [ binaryen.i32, dataViewTypeInfo.typeRef, arrayBufferTypeInfo.typeRef, binaryen.i32, binaryen.i32, binaryen.i32, binaryen.i32, i8ArrayTypeInfo.typeRef, ], dataView_setInt8(module), ); module.addFunction( UtilFuncs.getBuiltinClassMethodName(BuiltinNames.DATAVIEW, 'setUint8'), binaryen.createType([ emptyStructType.typeRef, emptyStructType.typeRef, binaryen.f64, binaryen.f64, ]), binaryen.none, [ binaryen.i32, dataViewTypeInfo.typeRef, arrayBufferTypeInfo.typeRef, binaryen.i32, binaryen.i32, binaryen.i32, binaryen.i32, i8ArrayTypeInfo.typeRef, ], dataView_setInt8(module), ); module.addFunction( UtilFuncs.getBuiltinClassMethodName(BuiltinNames.DATAVIEW, 'setInt16'), binaryen.createType([ emptyStructType.typeRef, emptyStructType.typeRef, binaryen.f64, binaryen.f64, binaryen.anyref, ]), binaryen.none, [ binaryen.i32, dataViewTypeInfo.typeRef, arrayBufferTypeInfo.typeRef, binaryen.i32, binaryen.i32, binaryen.i32, binaryen.i32, i8ArrayTypeInfo.typeRef, binaryen.i32, ], dataView_setInt16(module), ); module.addFunction( UtilFuncs.getBuiltinClassMethodName(BuiltinNames.DATAVIEW, 'setUint16'), binaryen.createType([ emptyStructType.typeRef, emptyStructType.typeRef, binaryen.f64, binaryen.f64, binaryen.anyref, ]), binaryen.none, [ binaryen.i32, dataViewTypeInfo.typeRef, arrayBufferTypeInfo.typeRef, binaryen.i32, binaryen.i32, binaryen.i32, binaryen.i32, i8ArrayTypeInfo.typeRef, binaryen.i32, ], dataView_setInt16(module), ); module.addFunction( UtilFuncs.getBuiltinClassMethodName(BuiltinNames.DATAVIEW, 'setInt32'), binaryen.createType([ emptyStructType.typeRef, emptyStructType.typeRef, binaryen.f64, binaryen.f64, binaryen.anyref, ]), binaryen.none, [ binaryen.i32, dataViewTypeInfo.typeRef, arrayBufferTypeInfo.typeRef, binaryen.i32, binaryen.i32, binaryen.i32, binaryen.i32, i8ArrayTypeInfo.typeRef, binaryen.i32, ], dataView_setInt32(module), ); module.addFunction( UtilFuncs.getBuiltinClassMethodName(BuiltinNames.DATAVIEW, 'setUint32'), binaryen.createType([ emptyStructType.typeRef, emptyStructType.typeRef, binaryen.f64, binaryen.f64, binaryen.anyref, ]), binaryen.none, [ binaryen.i32, dataViewTypeInfo.typeRef, arrayBufferTypeInfo.typeRef, binaryen.i32, binaryen.i32, binaryen.i32, binaryen.i32, i8ArrayTypeInfo.typeRef, binaryen.i32, ], dataView_setInt32(module), ); module.addFunction( UtilFuncs.getBuiltinClassMethodName( BuiltinNames.DATAVIEW, 'setFloat32', ), binaryen.createType([ emptyStructType.typeRef, emptyStructType.typeRef, binaryen.f64, binaryen.f64, binaryen.anyref, ]), binaryen.none, [ binaryen.i32, dataViewTypeInfo.typeRef, arrayBufferTypeInfo.typeRef, binaryen.i32, binaryen.i32, binaryen.i32, binaryen.i32, i8ArrayTypeInfo.typeRef, binaryen.i32, ], dataView_setFloat32(module), ); module.addFunction( UtilFuncs.getBuiltinClassMethodName( BuiltinNames.DATAVIEW, 'setFloat64', ), binaryen.createType([ emptyStructType.typeRef, emptyStructType.typeRef, binaryen.f64, binaryen.f64, binaryen.anyref, ]), binaryen.none, [ binaryen.i32, dataViewTypeInfo.typeRef, arrayBufferTypeInfo.typeRef, binaryen.i32, binaryen.i32, binaryen.i32, binaryen.i64, i8ArrayTypeInfo.typeRef, binaryen.i32, ], dataView_setFloat64(module), ); module.addFunction( UtilFuncs.getBuiltinClassMethodName(BuiltinNames.DATAVIEW, 'getInt8'), binaryen.createType([ emptyStructType.typeRef, emptyStructType.typeRef, binaryen.f64, ]), binaryen.f64, [ binaryen.i32, dataViewTypeInfo.typeRef, arrayBufferTypeInfo.typeRef, binaryen.i32, binaryen.i32, binaryen.i32, i8ArrayTypeInfo.typeRef, binaryen.i32, ], dataView_getInt8(module, true), ); module.addFunction( UtilFuncs.getBuiltinClassMethodName(BuiltinNames.DATAVIEW, 'getUint8'), binaryen.createType([ emptyStructType.typeRef, emptyStructType.typeRef, binaryen.f64, ]), binaryen.f64, [ binaryen.i32, dataViewTypeInfo.typeRef, arrayBufferTypeInfo.typeRef, binaryen.i32, binaryen.i32, binaryen.i32, i8ArrayTypeInfo.typeRef, binaryen.i32, ], dataView_getInt8(module, false), ); module.addFunction( UtilFuncs.getBuiltinClassMethodName(BuiltinNames.DATAVIEW, 'getInt16'), binaryen.createType([ emptyStructType.typeRef, emptyStructType.typeRef, binaryen.f64, binaryen.anyref, ]), binaryen.f64, [ binaryen.i32, dataViewTypeInfo.typeRef, arrayBufferTypeInfo.typeRef, binaryen.i32, binaryen.i32, binaryen.i32, i8ArrayTypeInfo.typeRef, binaryen.i32, binaryen.i32, ], dataView_getInt16(module, true), ); module.addFunction( UtilFuncs.getBuiltinClassMethodName(BuiltinNames.DATAVIEW, 'getUint16'), binaryen.createType([ emptyStructType.typeRef, emptyStructType.typeRef, binaryen.f64, binaryen.anyref, ]), binaryen.f64, [ binaryen.i32, dataViewTypeInfo.typeRef, arrayBufferTypeInfo.typeRef, binaryen.i32, binaryen.i32, binaryen.i32, i8ArrayTypeInfo.typeRef, binaryen.i32, binaryen.i32, ], dataView_getInt16(module, false), ); module.addFunction( UtilFuncs.getBuiltinClassMethodName(BuiltinNames.DATAVIEW, 'getInt32'), binaryen.createType([ emptyStructType.typeRef, emptyStructType.typeRef, binaryen.f64, binaryen.anyref, ]), binaryen.f64, [ binaryen.i32, dataViewTypeInfo.typeRef, arrayBufferTypeInfo.typeRef, binaryen.i32, binaryen.i32, binaryen.i32, i8ArrayTypeInfo.typeRef, binaryen.i32, binaryen.i32, ], dataView_getInt32(module, true), ); module.addFunction( UtilFuncs.getBuiltinClassMethodName(BuiltinNames.DATAVIEW, 'getUint32'), binaryen.createType([ emptyStructType.typeRef, emptyStructType.typeRef, binaryen.f64, binaryen.anyref, ]), binaryen.f64, [ binaryen.i32, dataViewTypeInfo.typeRef, arrayBufferTypeInfo.typeRef, binaryen.i32, binaryen.i32, binaryen.i32, i8ArrayTypeInfo.typeRef, binaryen.i32, binaryen.i32, ], dataView_getInt32(module, false), ); module.addFunction( UtilFuncs.getBuiltinClassMethodName( BuiltinNames.DATAVIEW, 'getFloat32', ), binaryen.createType([ emptyStructType.typeRef, emptyStructType.typeRef, binaryen.f64, binaryen.anyref, ]), binaryen.f64, [ binaryen.i32, dataViewTypeInfo.typeRef, arrayBufferTypeInfo.typeRef, binaryen.i32, binaryen.i32, binaryen.i32, i8ArrayTypeInfo.typeRef, binaryen.i32, binaryen.i32, ], dataView_getFloat32(module), ); module.addFunction( UtilFuncs.getBuiltinClassMethodName( BuiltinNames.DATAVIEW, 'getFloat64', ), binaryen.createType([ emptyStructType.typeRef, emptyStructType.typeRef, binaryen.f64, binaryen.anyref, ]), binaryen.f64, [ binaryen.i32, dataViewTypeInfo.typeRef, arrayBufferTypeInfo.typeRef, binaryen.i32, binaryen.i32, binaryen.i32, i8ArrayTypeInfo.typeRef, binaryen.i32, binaryen.i64, ], dataView_getFloat64(module), ); module.addFunction( UtilFuncs.getBuiltinClassMethodName( BuiltinNames.STRINGCONSTRCTOR, 'fromCharCode', ), binaryen.createType([ emptyStructType.typeRef, emptyStructType.typeRef, numberArrayStructTypeInfo.typeRef, ]), binaryen.stringref, [binaryen.i32, binaryen.i32], string_fromCharCode(module), ); module.addFunction( BuiltinNames.getTupleField, binaryen.createType([ i32ArrayTypeInfo.typeRef, binaryen.i32, binaryen.anyref, ]), binaryen.anyref, [binaryen.i32, binaryen.i32, binaryen.i32, binaryen.anyref], WASMStruct_get_field(module), ); } function addArrayMethod( module: binaryen.Module, method: string, nameMap: BuiltinNames.GenericFuncName, commonGenericApi: boolean, /* use null to represent generic type */ paramTypes: (binaryen.Type | null)[], returnType: binaryen.Type | null, ) { const wasmTypeMap: any = { i32: binaryen.i32, i64: binaryen.i64, f32: binaryen.f32, f64: binaryen.f64, anyref: binaryen.anyref, }; for (const [key, value] of Object.entries(nameMap)) { if (key === 'generic') { continue; } module.addFunctionImport( UtilFuncs.getFuncName(BuiltinNames.builtinModuleName, value), 'env', commonGenericApi ? `array_${method}_generic` : `array_${method}_${key}`, binaryen.createType([ emptyStructType.typeRef, ...paramTypes.map((p) => { if (p === null) { return wasmTypeMap[key]; } return p; }), ]), returnType !== null ? returnType : wasmTypeMap[key], ); } } ================================================ FILE: src/backend/binaryen/lib/interface/meta.c ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include #include #include /** * MetaProperty record the properties information of object * name: property name * flag_and_index: flag and index of the property * type: type of the property, represent by property's type id */ typedef struct MetaProperty { char *name; int flag_and_index; int type; } MetaProperty; /** * Meta record type id and the properties information of object * type_id: type id of the object * count: number of properties * properties: properties information */ typedef struct Meta { int type_id; int impl_id; int count; /* property includes field, method and accessor */ MetaProperty properties[0]; } Meta; enum propertyFlag { FIELD = 0, METHOD = 1, GETTER = 2, SETTER = 3, ALL = 4, }; #define META_FLAG_MASK 0x0000000F #define META_INDEX_MASK 0xFFFFFFF0 /* find property index based on prop_name*/ int find_property_flag_and_index(Meta *meta, char *prop_name, enum propertyFlag flag) { MetaProperty prop; int target_flag = flag & META_FLAG_MASK; int all_flag = ALL & META_FLAG_MASK; for (int i = 0; i < meta->count; i++) { prop = meta->properties[i]; if (strcmp(prop.name, prop_name) == 0) { if (target_flag == all_flag) { return prop.flag_and_index; } else if ((prop.flag_and_index & META_FLAG_MASK) == target_flag) { return prop.flag_and_index; } } } return -1; } /* find property type based on prop_name*/ int find_property_type(Meta *meta, char *prop_name, enum propertyFlag flag) { MetaProperty prop; int target_flag = flag & META_FLAG_MASK; int all_flag = ALL & META_FLAG_MASK; for (int i = 0; i < meta->count; i++) { prop = meta->properties[i]; if (strcmp(prop.name, prop_name) == 0) { if (target_flag == all_flag) { return prop.type; } else if ((prop.flag_and_index & META_FLAG_MASK) == target_flag) { return prop.type; } } } return -1; } ================================================ FILE: src/backend/binaryen/memory.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import binaryen from 'binaryen'; import { assert } from 'console'; import { BuiltinNames } from '../../../lib/builtin/builtin_name.js'; import Long from 'long'; function i64New(lowBits: number, highBits: number) { return Long.fromBits(lowBits, highBits); } function i64Add(leftI64: Long, rightI64: Long) { return leftI64.add(rightI64); } function i64Align(i64Value: Long, alignment: number) { const maskNumber = alignment - 1; assert(alignment && (alignment & maskNumber) == 0); const i64Mask = Long.fromInt(maskNumber); return i64Value.add(i64Mask).and(i64Mask.not()); } export function initGlobalOffset(module: binaryen.Module, usedMemory: number) { let memoryOffset = i64New(usedMemory, 0); // add global dataEnd module.addGlobal( BuiltinNames.dataEnd, binaryen.i32, false, module.i32.const(memoryOffset.low), ); module.addGlobalExport(BuiltinNames.dataEnd, BuiltinNames.dataEnd); memoryOffset = i64Align( i64Add(memoryOffset, i64New(BuiltinNames.stackSize, 0)), Math.ceil(BuiltinNames.byteSize / 8), ); // add global stackPointer module.addGlobal( BuiltinNames.stackPointer, binaryen.i32, true, module.i32.const(memoryOffset.low), ); // add global heapBase module.addGlobal( BuiltinNames.heapBase, binaryen.i32, false, module.i32.const(memoryOffset.low), ); module.addGlobalExport(BuiltinNames.heapBase, BuiltinNames.heapBase); } export const memoryAlignment = 4; export function initDefaultMemory( module: binaryen.Module, segments: binaryen.MemorySegment[], ): void { module.setMemory( BuiltinNames.memInitialPages, BuiltinNames.memMaximumPages, 'default', segments, ); } export function initDefaultTable(module: binaryen.Module): void { module.addTable(BuiltinNames.extrefTable, 0, -1, binaryen.anyref); } ================================================ FILE: src/backend/binaryen/utils.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import binaryen from 'binaryen'; import * as binaryenCAPI from './glue/binaryen.js'; import ts from 'typescript'; import { BuiltinNames } from '../../../lib/builtin/builtin_name.js'; import { UnimplementError } from '../../error.js'; import { dyntype, structdyn } from './lib/dyntype/utils.js'; import { NativeSignature, SemanticsKind, } from '../../semantics/semantics_nodes.js'; import { EnumType, ObjectType, Primitive, PrimitiveType, TypeParameterType, UnionType, ValueType, ValueTypeKind, } from '../../semantics/value_types.js'; import { StringRefMeatureOp, StringRefNewOp, arrayToPtr, baseVtableType, emptyStructType, generateArrayStructTypeInfo, stringrefArrayType, } from './glue/transform.js'; import { stringTypeInfo, i8ArrayTypeInfo, stringArrayTypeInfo, stringArrayStructTypeInfo, stringrefArrayStructTypeInfo, arrayBufferTypeInfo, anyArrayTypeInfo, } from './glue/packType.js'; import { PredefinedTypeId, SourceLocation, getBuiltInFuncName, } from '../../utils.js'; import { NewLiteralArrayValue, SemanticsValue, SemanticsValueKind, } from '../../semantics/value.js'; import { ObjectDescriptionType } from '../../semantics/runtime.js'; import { getConfig } from '../../../config/config_mgr.js'; import { memoryAlignment } from './memory.js'; import { assert } from 'console'; /** typeof an any type object */ export const enum DynType { DynUnknown, DynNull, DynUndefined, DynObject, DynBoolean, DynNumber, DynString, DynFunction, DynSymbol, DynBigInt, DynExtRefObj, DynExtRefFunc, DynExtRefInfc, DynExtRefArray, } export interface FlattenLoop { label: string; continueLabel?: string; condition?: binaryen.ExpressionRef; statements: binaryen.ExpressionRef; incrementor?: binaryen.ExpressionRef; } export interface IfStatementInfo { condition: binaryen.ExpressionRef; ifTrue: binaryen.ExpressionRef; ifFalse: binaryen.ExpressionRef; } export interface BackendLocalVar { type: binaryen.Type; index: number; } export enum ItableFlag { FIELD = 0, METHOD, GETTER, SETTER, ALL, } export const enum StructFieldIndex { VTABLE_INDEX = 0, } export const enum VtableFieldIndex { META_INDEX = 0, } export const SIZE_OF_META_FIELD = 12; export const enum MetaDataOffset { TYPE_ID_OFFSET = 0, IMPL_ID_OFFSET = 4, COUNT_OFFSET = 8, FIELDS_PTR_OFFSET = 12, } export const enum MetaPropertyOffset { NAME_OFFSET = 0, FLAG_AND_INDEX_OFFSET = 4, TYPE_OFFSET = 8, } export interface SourceMapLoc { location: SourceLocation; ref: binaryen.ExpressionRef; } export const enum NativeSignatureConversion { INVALID, ARRAYBUFFER_TO_I32, I32_TO_ARRAYBUFFER, I32_TO_I32, STRING_TO_STRING, STRING_TO_I32, } export const META_FLAG_MASK = 0x0000000f; export const META_INDEX_MASK = 0xfffffff0; export namespace UtilFuncs { export function getFuncName( moduleName: string, funcName: string, delimiter = '|', ) { return moduleName.concat(delimiter).concat(funcName); } export function getBuiltinClassCtorName(className: string) { return BuiltinNames.builtinModuleName .concat(BuiltinNames.moduleDelimiter) .concat(className) .concat(BuiltinNames.moduleDelimiter) .concat(BuiltinNames.ctorName); } export function getBuiltinClassMethodName( className: string, methodName: string, ) { return BuiltinNames.builtinModuleName .concat(BuiltinNames.moduleDelimiter) .concat(className) .concat(BuiltinNames.moduleDelimiter) .concat(methodName); } export function getLastElemOfBuiltinName(builtinName: string) { const levelNames = builtinName.split(BuiltinNames.moduleDelimiter); return levelNames[levelNames.length - 1]; } export function addWatFuncs( watModule: binaryen.Module, funcName: string, curModule: binaryen.Module, ) { const funcRef = watModule.getFunction(funcName); const funcInfo = binaryen.getFunctionInfo(funcRef); curModule.addFunction( funcInfo.name, funcInfo.params, funcInfo.results, funcInfo.vars, curModule.copyExpression(funcInfo.body), ); } export function isSupportedStringOP(opKind: ts.SyntaxKind) { switch (opKind) { case ts.SyntaxKind.ExclamationEqualsToken: case ts.SyntaxKind.ExclamationEqualsEqualsToken: case ts.SyntaxKind.EqualsEqualsToken: case ts.SyntaxKind.EqualsEqualsEqualsToken: case ts.SyntaxKind.PlusToken: case ts.SyntaxKind.BarBarToken: return true; default: return false; } } export const wasmStringMap = new Map(); export function getCString(str: string) { if (wasmStringMap.has(str)) { return wasmStringMap.get(str) as number; } const wasmStr = binaryenCAPI._malloc(str.length + 1); let index = wasmStr; // consider UTF-8 only for (let i = 0; i < str.length; i++) { binaryenCAPI.__i32_store8(index++, str.codePointAt(i) as number); } binaryenCAPI.__i32_store8(index, 0); wasmStringMap.set(str, wasmStr); return wasmStr; } export function clearWasmStringMap() { wasmStringMap.clear(); } export function utf16ToUtf8(utf16String: string): string { let utf8String = ''; for (let i = 0; i < utf16String.length; i++) { const charCode = utf16String.charCodeAt(i); if (charCode <= 0x7f) { utf8String += String.fromCharCode(charCode); } else if (charCode <= 0x7ff) { utf8String += String.fromCharCode( 0xc0 | ((charCode >> 6) & 0x1f), ); utf8String += String.fromCharCode(0x80 | (charCode & 0x3f)); } else { utf8String += String.fromCharCode( 0xe0 | ((charCode >> 12) & 0x0f), ); utf8String += String.fromCharCode( 0x80 | ((charCode >> 6) & 0x3f), ); utf8String += String.fromCharCode(0x80 | (charCode & 0x3f)); } } return utf8String; } } export namespace FunctionalFuncs { /* We need to get the dyntype context again and again, so we cache it here and don't call module.global.get every time */ let dyntypeContextRef: binaryen.ExpressionRef | undefined; export function getEmptyRef(module: binaryen.Module) { return binaryenCAPI._BinaryenRefNull( module.ptr, emptyStructType.typeRef, ); } export function resetDynContextRef() { dyntypeContextRef = undefined; } export function getDynContextRef(module: binaryen.Module) { if (!dyntypeContextRef) { /* module.global.get will cause memory leak issue, so we use C-API instead */ dyntypeContextRef = binaryenCAPI._BinaryenGlobalGet( module.ptr, UtilFuncs.getCString(dyntype.dyntype_context), dyntype.dyn_ctx_t, ); } return dyntypeContextRef; } export function flattenLoopStatement( module: binaryen.Module, loopStatementInfo: FlattenLoop, kind: SemanticsKind, ): binaryen.ExpressionRef { const condition = loopStatementInfo.condition || module.i32.const(1); const ifStatementInfo: IfStatementInfo = { condition: condition, ifTrue: binaryen.none, ifFalse: binaryen.none, }; const stmts = loopStatementInfo.continueLabel ? module.block(loopStatementInfo.continueLabel, [ loopStatementInfo.statements, ]) : loopStatementInfo.statements; if (kind !== SemanticsKind.DOWHILE) { const ifTrueBlockArray: binaryen.ExpressionRef[] = []; if (loopStatementInfo.statements !== binaryen.none) { ifTrueBlockArray.push(stmts); } if (kind === SemanticsKind.FOR && loopStatementInfo.incrementor) { ifTrueBlockArray.push( loopStatementInfo.incrementor, ); } ifTrueBlockArray.push(module.br(loopStatementInfo.label)); const ifTrueBlock = module.block(null, ifTrueBlockArray); ifStatementInfo.ifTrue = ifTrueBlock; return module.if(ifStatementInfo.condition, ifStatementInfo.ifTrue); } else { ifStatementInfo.ifTrue = module.br(loopStatementInfo.label); const blockArray: binaryen.ExpressionRef[] = []; if (loopStatementInfo.statements !== binaryen.none) { blockArray.push(stmts); } const ifExpression = module.if( ifStatementInfo.condition, ifStatementInfo.ifTrue, ); blockArray.push(ifExpression); return module.block(null, blockArray); } } export function getVarDefaultValue( module: binaryen.Module, type: ValueType, defaultValue?: binaryen.ExpressionRef, ): binaryen.ExpressionRef { if (defaultValue) { return defaultValue; } const typeKind = type.kind; switch (typeKind) { case ValueTypeKind.NUMBER: return module.f64.const(0); case ValueTypeKind.INT: case ValueTypeKind.BOOLEAN: return module.i32.const(0); case ValueTypeKind.ENUM: return getVarDefaultValue(module, (type).memberType); case ValueTypeKind.WASM_I64: return module.i64.const(0, 0); case ValueTypeKind.WASM_F32: return module.f32.const(0); default: return getEmptyRef(module); } } /** for non-optional, return the type itself * for optional type(T|undefined) type, return the type without undefined */ export function getStaticType(type: ValueType) { if (type instanceof UnionType) { if (type.types.size === 2 && type.types.has(Primitive.Undefined)) { for (const t of type.types) { if (t !== Primitive.Undefined) { return t; } } } } return type; } export function isUnionWithUndefined(type: ValueType) { return ( type instanceof UnionType && type.types.size == 2 && type.types.has(Primitive.Undefined) ); } export function generateStringForStructArrayStr( module: binaryen.Module, value: string, ) { const valueLen = value.length; let strRelLen = valueLen; const charArray = []; for (let i = 0; i < valueLen; i++) { const codePoint = value.codePointAt(i)!; if (codePoint > 0xffff) { i++; strRelLen--; } charArray.push(module.i32.const(codePoint)); } const valueContent = binaryenCAPI._BinaryenArrayNewFixed( module.ptr, i8ArrayTypeInfo.heapTypeRef, arrayToPtr(charArray).ptr, strRelLen, ); const wasmStringValue = binaryenCAPI._BinaryenStructNew( module.ptr, arrayToPtr([module.i32.const(0), valueContent]).ptr, 2, stringTypeInfo.heapTypeRef, ); return wasmStringValue; } export function generateStringForStringref( module: binaryen.Module, ptr: binaryen.ExpressionRef, len: binaryen.ExpressionRef, ) { return binaryenCAPI._BinaryenStringNew( module.ptr, StringRefNewOp.UTF8, ptr, len, 0, 0, false, ); } export function generateDynNumber( module: binaryen.Module, dynValue: binaryen.ExpressionRef, ) { return module.call( dyntype.dyntype_new_number, [getDynContextRef(module), convertTypeToF64(module, dynValue)], dyntype.dyn_value_t, ); } export function generateDynBoolean( module: binaryen.Module, dynValue: binaryen.ExpressionRef, ) { return module.call( dyntype.dyntype_new_boolean, [getDynContextRef(module), dynValue], dyntype.dyn_value_t, ); } export function generateDynString( module: binaryen.Module, dynValue: binaryen.ExpressionRef, ) { return module.call( dyntype.dyntype_new_string, [getDynContextRef(module), dynValue], dyntype.dyn_value_t, ); } export function generateDynNull(module: binaryen.Module) { return module.call( dyntype.dyntype_new_null, [getDynContextRef(module)], dyntype.dyn_value_t, ); } export function isDynUndefined( module: binaryen.Module, valueRef: binaryen.ExpressionRef, ) { return module.call( dyntype.dyntype_is_undefined, [getDynContextRef(module), valueRef], binaryen.i32, ); } export function generateDynUndefined(module: binaryen.Module) { return module.call( dyntype.dyntype_new_undefined, [getDynContextRef(module)], dyntype.dyn_value_t, ); } export function generateDynArray( module: binaryen.Module, arrLenRef: binaryen.ExpressionRef, ) { return module.call( dyntype.dyntype_new_array, [getDynContextRef(module), arrLenRef], dyntype.dyn_value_t, ); } export function generateDynObj(module: binaryen.Module) { return module.call( dyntype.dyntype_new_object, [getDynContextRef(module)], dyntype.dyn_value_t, ); } export function setDynArrElem( module: binaryen.Module, arrValueRef: binaryen.ExpressionRef, idxRef: binaryen.ExpressionRef, elemValueRef: binaryen.ExpressionRef, ) { return module.call( dyntype.dyntype_set_elem, [getDynContextRef(module), arrValueRef, idxRef, elemValueRef], dyntype.cvoid, ); } export function getDynArrElem( module: binaryen.Module, arrValueRef: binaryen.ExpressionRef, idxRef: binaryen.ExpressionRef, ) { return module.call( dyntype.dyntype_get_elem, [getDynContextRef(module), arrValueRef, idxRef], dyntype.dyn_value_t, ); } export function setDynObjProp( module: binaryen.Module, objValueRef: binaryen.ExpressionRef, propNameRef: binaryen.ExpressionRef, propValueRef: binaryen.ExpressionRef, ) { return module.call( dyntype.dyntype_set_property, [getDynContextRef(module), objValueRef, propNameRef, propValueRef], dyntype.int, ); } export function getDynObjProp( module: binaryen.Module, objValueRef: binaryen.ExpressionRef, propNameRef: binaryen.ExpressionRef, ) { return module.call( dyntype.dyntype_get_property, [getDynContextRef(module), objValueRef, propNameRef], dyntype.dyn_value_t, ); } export function getObjKeys( module: binaryen.Module, objValueRef: binaryen.ExpressionRef, ) { return module.call( dyntype.dyntype_get_keys, [getDynContextRef(module), objValueRef], dyntype.dyn_value_t, ); } export function generateDynExtref( module: binaryen.Module, dynValue: binaryen.ExpressionRef, tagRef: binaryen.ExpressionRef, ) { /* table type is anyref, no need to cast */ const dynFuncName: string = getBuiltInFuncName(BuiltinNames.newExtRef); /* call newExtRef */ const newExternRef = module.call( dynFuncName, [ module.global.get(dyntype.dyntype_context, dyntype.dyn_ctx_t), tagRef, dynValue, ], binaryen.anyref, ); return newExternRef; } export function getExtTagRefByTypeKind( module: binaryen.Module, typeKind: ValueTypeKind, ) { let extObjKind: dyntype.ExtObjKind = 0; switch (typeKind) { case ValueTypeKind.OBJECT: case ValueTypeKind.INTERFACE: { extObjKind = dyntype.ExtObjKind.ExtObj; break; } case ValueTypeKind.FUNCTION: { extObjKind = dyntype.ExtObjKind.ExtFunc; break; } case ValueTypeKind.ARRAY: { extObjKind = dyntype.ExtObjKind.ExtArray; break; } } return module.i32.const(extObjKind); } export function getExtTagRefByTypeIdRef( module: binaryen.Module, typeIdRef: binaryen.ExpressionRef, ) { return module.if( module.i32.eq( typeIdRef, module.i32.const(PredefinedTypeId.FUNCTION), ), module.i32.const(dyntype.ExtObjKind.ExtFunc), module.if( module.i32.eq( typeIdRef, module.i32.const(PredefinedTypeId.ARRAY), ), module.i32.const(dyntype.ExtObjKind.ExtArray), module.i32.const(dyntype.ExtObjKind.ExtObj), ), ); } export function generateDynExtrefByTypeKind( module: binaryen.Module, dynValue: binaryen.ExpressionRef, extrefTypeKind: ValueTypeKind, ) { /** workaround: Now Method's type in interface is always function type, but because of * optional, it can be anyref, so here also need to check if it is anyref */ const type = binaryen.getExpressionType(dynValue); if ( extrefTypeKind === ValueTypeKind.FUNCTION && type === binaryen.anyref ) { return dynValue; } const tagRef = getExtTagRefByTypeKind(module, extrefTypeKind); return generateDynExtref(module, dynValue, tagRef); } export function generateCondition( module: binaryen.Module, exprRef: binaryen.ExpressionRef, srckind: ValueTypeKind, ) { const type = binaryen.getExpressionType(exprRef); let res: binaryen.ExpressionRef; if (binaryen.getExpressionType(exprRef) === binaryen.i32) { /* Sometimes the value has already been casted, no need to cast again */ return exprRef; } if (srckind === ValueTypeKind.BOOLEAN) { res = exprRef; } else if (srckind === ValueTypeKind.NUMBER) { const n0 = module.f64.ne(exprRef, module.f64.const(0)); const nNaN = module.f64.eq(exprRef, exprRef); res = module.i32.and(n0, nNaN); } else if (srckind === ValueTypeKind.INT) { const n0 = module.i32.ne(exprRef, module.i32.const(0)); const nNaN = module.i32.eq(exprRef, exprRef); res = module.i32.and(n0, nNaN); } else if (srckind === ValueTypeKind.WASM_I64) { const n0 = module.i64.ne(exprRef, module.i64.const(0, 0)); const nNaN = module.i64.eq(exprRef, exprRef); res = module.i32.and(n0, nNaN); } else if (srckind === ValueTypeKind.WASM_F32) { const n0 = module.f32.ne(exprRef, module.f32.const(0)); const nNaN = module.f32.eq(exprRef, exprRef); res = module.i32.and(n0, nNaN); } else if ( srckind === ValueTypeKind.ANY || srckind === ValueTypeKind.UNDEFINED || srckind === ValueTypeKind.UNION || // for class/infc method, the ValueTypeKind cant represent the wasm type type === binaryen.anyref ) { const targetFunc = getBuiltInFuncName(BuiltinNames.anyrefCond); res = module.call(targetFunc, [exprRef], binaryen.i32); } else if (srckind === ValueTypeKind.STRING) { // '' => false, '123' => true let len: binaryen.ExpressionRef; if (getConfig().enableStringRef) { len = binaryenCAPI._BinaryenStringMeasure( module.ptr, StringRefMeatureOp.WTF16, exprRef, ); } else { const strArray = binaryenCAPI._BinaryenStructGet( module.ptr, 1, exprRef, i8ArrayTypeInfo.typeRef, false, ); len = binaryenCAPI._BinaryenArrayLen(module.ptr, strArray); } res = module.i32.ne(len, module.i32.const(0)); } else { res = module.i32.eqz( binaryenCAPI._BinaryenRefIsNull(module.ptr, exprRef), ); } return res; } export function unboxAny( module: binaryen.Module, anyExprRef: binaryen.ExpressionRef, typeKind: ValueTypeKind, wasmType: binaryen.Type, ) { switch (typeKind) { case ValueTypeKind.NUMBER: case ValueTypeKind.INT: case ValueTypeKind.WASM_F32: case ValueTypeKind.WASM_I64: case ValueTypeKind.BOOLEAN: case ValueTypeKind.STRING: case ValueTypeKind.NULL: case ValueTypeKind.ANY: case ValueTypeKind.UNION: case ValueTypeKind.UNDEFINED: case ValueTypeKind.TYPE_PARAMETER: return unboxAnyToBase(module, anyExprRef, typeKind); case ValueTypeKind.INTERFACE: case ValueTypeKind.ARRAY: case ValueTypeKind.OBJECT: case ValueTypeKind.FUNCTION: { return unboxAnyToExtref(module, anyExprRef, wasmType); } default: throw Error(`unboxAny: error kind ${typeKind}`); } } export function unboxAnyToBase( module: binaryen.Module, anyExprRef: binaryen.ExpressionRef, typeKind: ValueTypeKind, ) { let cvtFuncName = ''; let binaryenType: binaryen.Type; if ( typeKind === ValueTypeKind.ANY || typeKind === ValueTypeKind.UNION || typeKind === ValueTypeKind.TYPE_PARAMETER ) { return anyExprRef; } if (typeKind === ValueTypeKind.NULL) { return getEmptyRef(module); } if (typeKind === ValueTypeKind.UNDEFINED) { return generateDynUndefined(module); } /* native API's dynamic params */ const dynParam = [getDynContextRef(module), anyExprRef]; switch (typeKind) { case ValueTypeKind.INT: case ValueTypeKind.WASM_I64: case ValueTypeKind.WASM_F32: case ValueTypeKind.NUMBER: { cvtFuncName = dyntype.dyntype_to_number; binaryenType = binaryen.f64; break; } case ValueTypeKind.BOOLEAN: { cvtFuncName = dyntype.dyntype_to_bool; binaryenType = binaryen.i32; /* Auto generate condition for boolean type */ return generateCondition(module, anyExprRef, ValueTypeKind.ANY); } case ValueTypeKind.RAW_STRING: case ValueTypeKind.STRING: { const wasmStringType = getConfig().enableStringRef ? binaryenCAPI._BinaryenTypeStringref() : stringTypeInfo.typeRef; cvtFuncName = dyntype.dyntype_toString; binaryenType = wasmStringType; break; } default: { throw Error( `unboxing any type to static type, unsupported static type : ${typeKind}`, ); } } const res = module.call(cvtFuncName, dynParam, binaryenType); switch (typeKind) { case ValueTypeKind.INT: return convertTypeToI32(module, res); case ValueTypeKind.WASM_I64: return convertTypeToI64(module, res); case ValueTypeKind.WASM_F32: return convertTypeToF32(module, res); default: return res; } } export function isBaseType( module: binaryen.Module, anyExprRef: binaryen.ExpressionRef, condFuncName: string, ) { return module.call( condFuncName, [getDynContextRef(module), anyExprRef], dyntype.bool, ); } export function convertTypeToI32( module: binaryen.Module, expression: binaryen.ExpressionRef, expressionType?: binaryen.Type, isSigned = true, ): binaryen.ExpressionRef { const exprType = expressionType ? expressionType : binaryen.getExpressionType(expression); switch (exprType) { case binaryen.f64: { return module.i32.wrap( convertTypeToI64( module, expression, binaryen.f64, isSigned, ), ); } case binaryen.f32: { return isSigned ? module.i32.trunc_s.f32(expression) : module.i32.trunc_u.f32(expression); } case binaryen.i64: { return module.i32.wrap(expression); } case binaryen.i32: { return expression; } } return binaryen.none; } export function convertTypeToI64( module: binaryen.Module, expression: binaryen.ExpressionRef, expressionType?: binaryen.Type, isSigned = true, ): binaryen.ExpressionRef { const exprType = expressionType ? expressionType : binaryen.getExpressionType(expression); switch (exprType) { case binaryen.f64: { return isSigned ? module.i64.trunc_s.f64(expression) : module.i64.trunc_u.f64(expression); } case binaryen.f32: { return isSigned ? module.i64.trunc_s.f32(expression) : module.i64.trunc_u.f32(expression); } case binaryen.i64: { return expression; } case binaryen.i32: { return isSigned ? module.i64.extend_s(expression) : module.i64.extend_u(expression); } } return binaryen.none; } export function convertTypeToF32( module: binaryen.Module, expression: binaryen.ExpressionRef, expressionType?: binaryen.Type, isSigned = true, ): binaryen.ExpressionRef { const exprType = expressionType ? expressionType : binaryen.getExpressionType(expression); switch (exprType) { case binaryen.f64: { return module.f32.demote(expression); } case binaryen.f32: { return expression; } case binaryen.i64: { return isSigned ? module.f32.convert_s.i64(expression) : module.f32.convert_u.i64(expression); } case binaryen.i32: { return isSigned ? module.f32.convert_s.i32(expression) : module.f32.convert_u.i32(expression); } } return binaryen.none; } export function convertTypeToF64( module: binaryen.Module, expression: binaryen.ExpressionRef, expressionType?: binaryen.Type, isSigned = true, ): binaryen.ExpressionRef { const exprType = expressionType ? expressionType : binaryen.getExpressionType(expression); switch (exprType) { case binaryen.f64: { return expression; } case binaryen.f32: { return module.f64.promote(expression); } case binaryen.i64: { return isSigned ? module.f64.convert_s.i64(expression) : module.f64.convert_u.i64(expression); } case binaryen.i32: { return isSigned ? module.f64.convert_s.i32(expression) : module.f64.convert_u.i32(expression); } } return binaryen.none; } export function unboxAnyToExtrefWithoutCast( module: binaryen.Module, anyExprRef: binaryen.ExpressionRef, ) { /* unbox to externalRef */ const tableIndex = module.call( dyntype.dyntype_to_extref, [getDynContextRef(module), anyExprRef], dyntype.int, ); const externalRef = module.table.get( BuiltinNames.extrefTable, tableIndex, binaryen.anyref, ); return externalRef; } export function unboxAnyToExtref( module: binaryen.Module, anyExprRef: binaryen.ExpressionRef, wasmType: binaryen.Type, ) { let value: binaryen.ExpressionRef; if (wasmType === binaryen.anyref) { /* if wasm type is anyref type, then value may be a pure Quickjs value */ value = anyExprRef; } else { value = binaryenCAPI._BinaryenRefCast( module.ptr, unboxAnyToExtrefWithoutCast(module, anyExprRef), wasmType, ); } return value; } export function boxToAny( module: binaryen.Module, valueRef: binaryen.ExpressionRef, value: SemanticsValue, arrLenRef?: binaryen.ExpressionRef, ) { let valueTypeKind = value.type.kind; /* value.type may be specialized, we should update the specialized type kind */ if (value.type instanceof TypeParameterType) { const specializedType = (value.type) .specialTypeArgument; if (specializedType) { valueTypeKind = specializedType.kind; } else { valueTypeKind = ValueTypeKind.ANY; } } if (value.type instanceof EnumType) { valueTypeKind = value.type.memberType.kind; } const semanticsValueKind = value.kind; switch (valueTypeKind) { case ValueTypeKind.NUMBER: case ValueTypeKind.INT: case ValueTypeKind.WASM_I64: case ValueTypeKind.WASM_F32: case ValueTypeKind.BOOLEAN: case ValueTypeKind.STRING: case ValueTypeKind.RAW_STRING: case ValueTypeKind.NULL: case ValueTypeKind.UNDEFINED: case ValueTypeKind.ANY: case ValueTypeKind.UNION: return boxBaseTypeToAny(module, valueRef, valueTypeKind); case ValueTypeKind.INTERFACE: case ValueTypeKind.ARRAY: case ValueTypeKind.OBJECT: { switch (semanticsValueKind) { case SemanticsValueKind.NEW_LITERAL_ARRAY: case SemanticsValueKind.NEW_LITERAL_OBJECT: return boxLiteralToAny(module, value, arrLenRef); default: { return boxNonLiteralToAny( module, valueRef, valueTypeKind, ); } } } case ValueTypeKind.FUNCTION: { return boxNonLiteralToAny(module, valueRef, valueTypeKind); } default: throw Error(`boxToAny: error kind ${valueTypeKind}`); } } export function boxBaseTypeToAny( module: binaryen.Module, valueRef: binaryen.ExpressionRef, valueTypeKind: ValueTypeKind, ): binaryen.ExpressionRef { switch (valueTypeKind) { case ValueTypeKind.NUMBER: case ValueTypeKind.INT: case ValueTypeKind.WASM_I64: case ValueTypeKind.WASM_F32: { const floatNumber = convertTypeToF64(module, valueRef); return generateDynNumber(module, floatNumber); } case ValueTypeKind.BOOLEAN: return generateDynBoolean(module, valueRef); case ValueTypeKind.RAW_STRING: case ValueTypeKind.STRING: { return generateDynString(module, valueRef); } case ValueTypeKind.NULL: return generateDynNull(module); case ValueTypeKind.UNDEFINED: return generateDynUndefined(module); case ValueTypeKind.UNION: case ValueTypeKind.ANY: return valueRef; default: throw Error(`boxBaseTypeToAny: error kind ${valueTypeKind}`); } } export function boxLiteralToAny( module: binaryen.Module, value: SemanticsValue, arrLenRef?: binaryen.ExpressionRef, ): binaryen.ExpressionRef { const valueTypeKind = value.type.kind; switch (valueTypeKind) { case ValueTypeKind.OBJECT: return generateDynObj(module); case ValueTypeKind.ARRAY: return generateDynArray(module, arrLenRef!); default: throw Error(`boxLiteralToAny: error kind ${valueTypeKind}`); } } export function boxNonLiteralToAny( module: binaryen.Module, valueRef: binaryen.ExpressionRef, valueTypeKind: ValueTypeKind, ): binaryen.ExpressionRef { switch (valueTypeKind) { case ValueTypeKind.NUMBER: case ValueTypeKind.INT: case ValueTypeKind.WASM_I64: case ValueTypeKind.WASM_F32: case ValueTypeKind.BOOLEAN: case ValueTypeKind.RAW_STRING: case ValueTypeKind.STRING: case ValueTypeKind.NULL: case ValueTypeKind.UNDEFINED: return boxBaseTypeToAny(module, valueRef, valueTypeKind); case ValueTypeKind.UNION: case ValueTypeKind.ANY: return valueRef; case ValueTypeKind.INTERFACE: case ValueTypeKind.ARRAY: case ValueTypeKind.OBJECT: case ValueTypeKind.FUNCTION: { return generateDynExtrefByTypeKind( module, valueRef, valueTypeKind, ); } default: throw Error(`boxNonLiteralToAny: error kind ${valueTypeKind}`); } } export function operateF64F64( module: binaryen.Module, leftValueRef: binaryen.ExpressionRef, rightValueRef: binaryen.ExpressionRef, opKind: ts.SyntaxKind, ) { switch (opKind) { case ts.SyntaxKind.PlusToken: { return module.f64.add(leftValueRef, rightValueRef); } case ts.SyntaxKind.MinusToken: { return module.f64.sub(leftValueRef, rightValueRef); } case ts.SyntaxKind.AsteriskToken: { return module.f64.mul(leftValueRef, rightValueRef); } case ts.SyntaxKind.SlashToken: { return module.f64.div(leftValueRef, rightValueRef); } case ts.SyntaxKind.GreaterThanToken: { return module.f64.gt(leftValueRef, rightValueRef); } case ts.SyntaxKind.GreaterThanEqualsToken: { return module.f64.ge(leftValueRef, rightValueRef); } case ts.SyntaxKind.GreaterThanGreaterThanToken: { return convertTypeToF64( module, module.i32.shr_s( convertTypeToI32(module, leftValueRef, binaryen.f64), convertTypeToI32(module, rightValueRef, binaryen.f64), ), binaryen.i32, ); } case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken: { return convertTypeToF64( module, module.i32.shr_u( convertTypeToI32(module, leftValueRef, binaryen.f64), convertTypeToI32(module, rightValueRef, binaryen.f64), ), binaryen.i32, false, ); } case ts.SyntaxKind.LessThanToken: { return module.f64.lt(leftValueRef, rightValueRef); } case ts.SyntaxKind.LessThanEqualsToken: { return module.f64.le(leftValueRef, rightValueRef); } case ts.SyntaxKind.LessThanLessThanToken: { return convertTypeToF64( module, module.i32.shl( convertTypeToI32(module, leftValueRef, binaryen.f64), convertTypeToI32(module, rightValueRef, binaryen.f64), ), binaryen.i32, ); } case ts.SyntaxKind.EqualsEqualsToken: case ts.SyntaxKind.EqualsEqualsEqualsToken: { return module.f64.eq(leftValueRef, rightValueRef); } case ts.SyntaxKind.ExclamationEqualsToken: case ts.SyntaxKind.ExclamationEqualsEqualsToken: { return module.f64.ne(leftValueRef, rightValueRef); } case ts.SyntaxKind.AmpersandAmpersandToken: { return module.select( convertTypeToI32(module, leftValueRef, binaryen.f64), rightValueRef, leftValueRef, binaryen.f64, ); } case ts.SyntaxKind.BarBarToken: { return module.select( convertTypeToI32(module, leftValueRef, binaryen.f64), leftValueRef, rightValueRef, binaryen.f64, ); } case ts.SyntaxKind.AmpersandToken: { return convertTypeToF64( module, module.i32.and( convertTypeToI32(module, leftValueRef, binaryen.f64), convertTypeToI32(module, rightValueRef, binaryen.f64), ), binaryen.i32, ); } case ts.SyntaxKind.BarToken: { return convertTypeToF64( module, module.i32.or( convertTypeToI32(module, leftValueRef, binaryen.f64), convertTypeToI32(module, rightValueRef, binaryen.f64), ), binaryen.i32, ); } case ts.SyntaxKind.PercentToken: { return convertTypeToF64( module, module.i64.rem_s( convertTypeToI64(module, leftValueRef, binaryen.f64), convertTypeToI64(module, rightValueRef, binaryen.f64), ), ); } case ts.SyntaxKind.CaretToken: { return convertTypeToF64( module, module.i32.xor( convertTypeToI32(module, leftValueRef, binaryen.f64), convertTypeToI32(module, rightValueRef, binaryen.f64), ), ); } default: throw new UnimplementError(`operateF64F64: ${opKind}`); } } export function operateStringString( module: binaryen.Module, leftValueRef: binaryen.ExpressionRef, rightValueRef: binaryen.ExpressionRef, opKind: ts.SyntaxKind, ) { let res: binaryen.ExpressionRef; switch (opKind) { case ts.SyntaxKind.ExclamationEqualsToken: case ts.SyntaxKind.ExclamationEqualsEqualsToken: case ts.SyntaxKind.EqualsEqualsToken: case ts.SyntaxKind.EqualsEqualsEqualsToken: { res = module.call( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringEQFuncName, ), [leftValueRef, rightValueRef], dyntype.bool, ); if ( opKind === ts.SyntaxKind.ExclamationEqualsToken || opKind === ts.SyntaxKind.ExclamationEqualsEqualsToken ) { res = module.i32.eqz(res); } break; } case ts.SyntaxKind.PlusToken: { if (getConfig().enableStringRef) { res = binaryenCAPI._BinaryenStringConcat( module.ptr, leftValueRef, rightValueRef, ); } else { const statementArray: binaryen.ExpressionRef[] = []; const arrayValue = binaryenCAPI._BinaryenArrayNewFixed( module.ptr, stringArrayTypeInfo.heapTypeRef, arrayToPtr([rightValueRef]).ptr, 1, ); const arrayStruct = binaryenCAPI._BinaryenStructNew( module.ptr, arrayToPtr([arrayValue, module.i32.const(1)]).ptr, 2, stringArrayStructTypeInfo.heapTypeRef, ); statementArray.push( module.call( getBuiltInFuncName( BuiltinNames.stringConcatFuncName, ), [getEmptyRef(module), leftValueRef, arrayStruct], stringTypeInfo.typeRef, ), ); res = module.block(null, statementArray); } break; } case ts.SyntaxKind.BarBarToken: { return module.select( generateCondition( module, leftValueRef, ValueTypeKind.STRING, ), leftValueRef, rightValueRef, stringTypeInfo.typeRef, ); } default: throw new UnimplementError( `operator doesn't support, ${opKind}`, ); } return res; } export function operateRefRef( module: binaryen.Module, leftExprRef: binaryen.ExpressionRef, rightExprRef: binaryen.ExpressionRef, operatorKind: ts.SyntaxKind, ) { switch (operatorKind) { case ts.SyntaxKind.EqualsEqualsToken: case ts.SyntaxKind.EqualsEqualsEqualsToken: { return binaryenCAPI._BinaryenRefEq( module.ptr, leftExprRef, rightExprRef, ); } case ts.SyntaxKind.AmpersandAmpersandToken: case ts.SyntaxKind.BarBarToken: { const leftIsValidRef = module.i32.eqz( binaryenCAPI._BinaryenRefIsNull(module.ptr, leftExprRef), ); const rightIsValidRef = module.i32.eqz( binaryenCAPI._BinaryenRefIsNull(module.ptr, rightExprRef), ); if (operatorKind == ts.SyntaxKind.AmpersandAmpersandToken) { return module.select( leftIsValidRef, rightIsValidRef, leftIsValidRef, binaryen.i32, ); } else { return module.select( leftIsValidRef, leftIsValidRef, rightIsValidRef, binaryen.i32, ); } } default: throw new UnimplementError( `operator doesn't support, ${operatorKind}`, ); } } export function operateF64I32( module: binaryen.Module, leftValueRef: binaryen.ExpressionRef, rightValueRef: binaryen.ExpressionRef, opKind: ts.SyntaxKind, ) { switch (opKind) { case ts.SyntaxKind.AmpersandAmpersandToken: { return module.select( convertTypeToI32(module, leftValueRef, binaryen.f64), rightValueRef, convertTypeToI32(module, leftValueRef, binaryen.f64), binaryen.i32, ); } case ts.SyntaxKind.BarBarToken: { return module.select( convertTypeToI32(module, leftValueRef, binaryen.f64), leftValueRef, convertTypeToF64(module, rightValueRef, binaryen.i32), binaryen.f64, ); } case ts.SyntaxKind.EqualsEqualsToken: case ts.SyntaxKind.EqualsEqualsEqualsToken: { return module.f64.eq( convertTypeToF64(module, leftValueRef), convertTypeToF64(module, rightValueRef), ); } default: throw new UnimplementError( `operator doesn't support, ${opKind}`, ); } } export function operateI32F64( module: binaryen.Module, leftValueRef: binaryen.ExpressionRef, rightValueRef: binaryen.ExpressionRef, opKind: ts.SyntaxKind, ) { switch (opKind) { case ts.SyntaxKind.AmpersandAmpersandToken: { const condition = Boolean(module.i32.eqz(leftValueRef)); if (condition) { return module.select( leftValueRef, convertTypeToI32(module, rightValueRef, binaryen.f64), leftValueRef, binaryen.i32, ); } else { return rightValueRef; } } case ts.SyntaxKind.BarBarToken: { // if left is false, then condition is true const condition = Boolean(module.i32.eqz(leftValueRef)); if (condition) { return rightValueRef; } else { return module.select( leftValueRef, convertTypeToF64(module, leftValueRef, binaryen.i32), rightValueRef, binaryen.f64, ); } } default: throw new UnimplementError( `operator doesn't support, ${opKind}`, ); } } export function operateI32I32( module: binaryen.Module, leftValueRef: binaryen.ExpressionRef, rightValueRef: binaryen.ExpressionRef, opKind: ts.SyntaxKind, ) { switch (opKind) { case ts.SyntaxKind.AmpersandAmpersandToken: { return module.select( leftValueRef, rightValueRef, leftValueRef, binaryen.i32, ); } case ts.SyntaxKind.BarBarToken: { return module.select( leftValueRef, leftValueRef, rightValueRef, binaryen.i32, ); } case ts.SyntaxKind.EqualsEqualsToken: case ts.SyntaxKind.EqualsEqualsEqualsToken: { return module.i32.eq(leftValueRef, rightValueRef); } case ts.SyntaxKind.ExclamationEqualsToken: case ts.SyntaxKind.ExclamationEqualsEqualsToken: { return module.i32.ne(leftValueRef, rightValueRef); } case ts.SyntaxKind.PlusToken: { return module.i32.add(leftValueRef, rightValueRef); } case ts.SyntaxKind.MinusToken: { return module.i32.sub(leftValueRef, rightValueRef); } case ts.SyntaxKind.AsteriskToken: { return module.i32.mul(leftValueRef, rightValueRef); } case ts.SyntaxKind.SlashToken: { return module.i32.div_s(leftValueRef, rightValueRef); } case ts.SyntaxKind.GreaterThanToken: { return module.i32.gt_s(leftValueRef, rightValueRef); } case ts.SyntaxKind.GreaterThanEqualsToken: { return module.i32.ge_s(leftValueRef, rightValueRef); } case ts.SyntaxKind.GreaterThanGreaterThanToken: { return module.i32.shr_s(leftValueRef, rightValueRef); } case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken: { return module.i32.shr_u(leftValueRef, rightValueRef); } case ts.SyntaxKind.LessThanToken: { return module.i32.lt_s(leftValueRef, rightValueRef); } case ts.SyntaxKind.LessThanEqualsToken: { return module.i32.le_s(leftValueRef, rightValueRef); } case ts.SyntaxKind.LessThanLessThanToken: { return module.i32.shl(leftValueRef, rightValueRef); } case ts.SyntaxKind.AmpersandToken: { return module.i32.and(leftValueRef, rightValueRef); } case ts.SyntaxKind.BarToken: { return module.i32.or(leftValueRef, rightValueRef); } case ts.SyntaxKind.PercentToken: { return module.i32.rem_s(leftValueRef, rightValueRef); } case ts.SyntaxKind.CaretToken: { return module.i32.xor(leftValueRef, rightValueRef); } default: throw new UnimplementError( `operator doesn't support, ${opKind}`, ); } } export function operateI64I64( module: binaryen.Module, leftValueRef: binaryen.ExpressionRef, rightValueRef: binaryen.ExpressionRef, opKind: ts.SyntaxKind, ) { switch (opKind) { case ts.SyntaxKind.AmpersandAmpersandToken: { return module.select( convertTypeToI32(module, leftValueRef, binaryen.i64), rightValueRef, leftValueRef, binaryen.i64, ); } case ts.SyntaxKind.BarBarToken: { return module.select( convertTypeToI32(module, leftValueRef, binaryen.i64), leftValueRef, rightValueRef, binaryen.i64, ); } case ts.SyntaxKind.EqualsEqualsToken: case ts.SyntaxKind.EqualsEqualsEqualsToken: { return module.i64.eq(leftValueRef, rightValueRef); } case ts.SyntaxKind.ExclamationEqualsToken: case ts.SyntaxKind.ExclamationEqualsEqualsToken: { return module.i64.ne(leftValueRef, rightValueRef); } case ts.SyntaxKind.PlusToken: { return module.i64.add(leftValueRef, rightValueRef); } case ts.SyntaxKind.MinusToken: { return module.i64.sub(leftValueRef, rightValueRef); } case ts.SyntaxKind.AsteriskToken: { return module.i64.mul(leftValueRef, rightValueRef); } case ts.SyntaxKind.SlashToken: { return module.i64.div_s(leftValueRef, rightValueRef); } case ts.SyntaxKind.GreaterThanToken: { return module.i64.gt_s(leftValueRef, rightValueRef); } case ts.SyntaxKind.GreaterThanEqualsToken: { return module.i64.ge_s(leftValueRef, rightValueRef); } case ts.SyntaxKind.GreaterThanGreaterThanToken: { return convertTypeToI64( module, module.i32.shr_s( convertTypeToI32(module, leftValueRef, binaryen.i64), convertTypeToI32(module, rightValueRef, binaryen.i64), ), binaryen.i32, ); } case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken: { return convertTypeToI64( module, module.i32.shr_u( convertTypeToI32(module, leftValueRef, binaryen.i64), convertTypeToI32(module, rightValueRef, binaryen.i64), ), binaryen.i32, ); } case ts.SyntaxKind.LessThanToken: { return module.i64.lt_s(leftValueRef, rightValueRef); } case ts.SyntaxKind.LessThanEqualsToken: { return module.i64.le_s(leftValueRef, rightValueRef); } case ts.SyntaxKind.LessThanLessThanToken: { return convertTypeToI64( module, module.i32.shl( convertTypeToI32(module, leftValueRef, binaryen.i64), convertTypeToI32(module, rightValueRef, binaryen.i64), ), binaryen.i32, ); } case ts.SyntaxKind.AmpersandToken: { return convertTypeToI64( module, module.i32.and( convertTypeToI32(module, leftValueRef, binaryen.i64), convertTypeToI32(module, rightValueRef, binaryen.i64), ), binaryen.i32, ); } case ts.SyntaxKind.BarToken: { return convertTypeToI64( module, module.i32.or( convertTypeToI32(module, leftValueRef, binaryen.i64), convertTypeToI32(module, rightValueRef, binaryen.i64), ), binaryen.i32, ); } case ts.SyntaxKind.PercentToken: { return module.i64.rem_s(leftValueRef, rightValueRef); } case ts.SyntaxKind.CaretToken: { return convertTypeToI64( module, module.i32.xor( convertTypeToI32(module, leftValueRef, binaryen.i64), convertTypeToI32(module, rightValueRef, binaryen.i64), ), binaryen.i32, ); } default: throw new UnimplementError( `operator doesn't support, ${opKind}`, ); } } export function operateF32F32( module: binaryen.Module, leftValueRef: binaryen.ExpressionRef, rightValueRef: binaryen.ExpressionRef, opKind: ts.SyntaxKind, ) { switch (opKind) { case ts.SyntaxKind.AmpersandAmpersandToken: { return module.select( convertTypeToI32(module, leftValueRef, binaryen.f32), rightValueRef, leftValueRef, binaryen.f32, ); } case ts.SyntaxKind.BarBarToken: { return module.select( convertTypeToI32(module, leftValueRef, binaryen.f32), leftValueRef, rightValueRef, binaryen.f32, ); } case ts.SyntaxKind.EqualsEqualsToken: case ts.SyntaxKind.EqualsEqualsEqualsToken: { return module.f32.eq(leftValueRef, rightValueRef); } case ts.SyntaxKind.ExclamationEqualsToken: case ts.SyntaxKind.ExclamationEqualsEqualsToken: { return module.f32.ne(leftValueRef, rightValueRef); } case ts.SyntaxKind.PlusToken: { return module.f32.add(leftValueRef, rightValueRef); } case ts.SyntaxKind.MinusToken: { return module.f32.sub(leftValueRef, rightValueRef); } case ts.SyntaxKind.AsteriskToken: { return module.f32.mul(leftValueRef, rightValueRef); } case ts.SyntaxKind.SlashToken: { return module.f32.div(leftValueRef, rightValueRef); } case ts.SyntaxKind.GreaterThanToken: { return module.f32.gt(leftValueRef, rightValueRef); } case ts.SyntaxKind.GreaterThanEqualsToken: { return module.f32.ge(leftValueRef, rightValueRef); } case ts.SyntaxKind.GreaterThanGreaterThanToken: { return convertTypeToF32( module, module.i32.shr_s( convertTypeToI32(module, leftValueRef, binaryen.f32), convertTypeToI32(module, rightValueRef, binaryen.f32), ), binaryen.i32, ); } case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken: { return convertTypeToF32( module, module.i32.shr_u( convertTypeToI32(module, leftValueRef, binaryen.f32), convertTypeToI32(module, rightValueRef, binaryen.f32), ), binaryen.i32, ); } case ts.SyntaxKind.LessThanToken: { return module.f32.lt(leftValueRef, rightValueRef); } case ts.SyntaxKind.LessThanEqualsToken: { return module.f32.le(leftValueRef, rightValueRef); } case ts.SyntaxKind.LessThanLessThanToken: { return convertTypeToF32( module, module.i64.shl( convertTypeToI64(module, leftValueRef, binaryen.f32), convertTypeToI64(module, rightValueRef, binaryen.f32), ), binaryen.i64, ); } case ts.SyntaxKind.AmpersandToken: { return convertTypeToF32( module, module.i64.and( convertTypeToI64(module, leftValueRef, binaryen.f32), convertTypeToI64(module, rightValueRef, binaryen.f32), ), binaryen.i64, ); } case ts.SyntaxKind.BarToken: { return convertTypeToF32( module, module.i64.or( convertTypeToI64(module, leftValueRef, binaryen.f32), convertTypeToI64(module, rightValueRef, binaryen.f32), ), binaryen.i64, ); } case ts.SyntaxKind.PercentToken: { return convertTypeToF32( module, module.i64.rem_s( convertTypeToI64(module, leftValueRef, binaryen.f32), convertTypeToI64(module, rightValueRef, binaryen.f32), ), ); } case ts.SyntaxKind.CaretToken: { return convertTypeToF32( module, module.i64.xor( convertTypeToI64(module, leftValueRef, binaryen.f32), convertTypeToI64(module, rightValueRef, binaryen.f32), ), ); } default: throw new UnimplementError( `operator doesn't support, ${opKind}`, ); } } export function treatAsAny(typeKind: ValueTypeKind) { if ( typeKind === ValueTypeKind.ANY || typeKind === ValueTypeKind.UNION ) { return true; } return false; } export function operateAnyAny( module: binaryen.Module, leftValueRef: binaryen.ExpressionRef, rightValueRef: binaryen.ExpressionRef, opKind: ts.SyntaxKind, ) { // TODO: not support ref type cmp let res: binaryen.ExpressionRef; switch (opKind) { case ts.SyntaxKind.EqualsEqualsToken: case ts.SyntaxKind.EqualsEqualsEqualsToken: case ts.SyntaxKind.LessThanEqualsToken: case ts.SyntaxKind.LessThanToken: case ts.SyntaxKind.GreaterThanEqualsToken: case ts.SyntaxKind.GreaterThanToken: case ts.SyntaxKind.ExclamationEqualsToken: case ts.SyntaxKind.ExclamationEqualsEqualsToken: { res = module.call( dyntype.dyntype_cmp, [ getDynContextRef(module), leftValueRef, rightValueRef, module.i32.const(opKind), ], binaryen.i32, ); break; } default: { res = operateStaticToDyn( module, leftValueRef, rightValueRef, opKind, ); break; } } return res; } export function operateStaticNullUndefined( module: binaryen.Module, leftValueType: ValueType, leftValueRef: binaryen.ExpressionRef, rightTypekind: ValueTypeKind, opKind: ts.SyntaxKind, ) { let res: binaryen.ExpressionRef; const isNotEqToken = opKind === ts.SyntaxKind.ExclamationEqualsToken || opKind === ts.SyntaxKind.ExclamationEqualsEqualsToken ? true : false; if (leftValueType.kind === rightTypekind) { res = isNotEqToken ? 0 : 1; } else { res = isNotEqToken ? 1 : 0; } res = module.i32.const(res); // let xx: A | null === null; // xx === null if ( !(leftValueType instanceof PrimitiveType) && rightTypekind === ValueTypeKind.NULL ) { res = module.ref.is_null(leftValueRef); if (isNotEqToken) { res = module.i32.eqz(res); } } return res; } export function operatorAnyStatic( module: binaryen.Module, leftValueRef: binaryen.ExpressionRef, leftValueType: ValueType, rightValueRef: binaryen.ExpressionRef, rightValueType: ValueType, opKind: ts.SyntaxKind, ) { let res: binaryen.ExpressionRef; const dynCtx = module.global.get( dyntype.dyntype_context, dyntype.dyn_ctx_t, ); switch (opKind) { case ts.SyntaxKind.EqualsEqualsToken: case ts.SyntaxKind.EqualsEqualsEqualsToken: case ts.SyntaxKind.ExclamationEqualsToken: case ts.SyntaxKind.ExclamationEqualsEqualsToken: { if ( leftValueType.kind === ValueTypeKind.NULL || rightValueType.kind === ValueTypeKind.NULL ) { const anyValueRef = leftValueType.kind === ValueTypeKind.NULL ? rightValueRef : leftValueRef; res = module.call( dyntype.dyntype_is_null, [dynCtx, anyValueRef], binaryen.i32, ); // TODO: ref.null need table.get support in native API } else if ( leftValueType.kind === ValueTypeKind.UNDEFINED || rightValueType.kind === ValueTypeKind.UNDEFINED ) { const anyValueRef = leftValueType.kind === ValueTypeKind.UNDEFINED ? rightValueRef : leftValueRef; res = isDynUndefined(module, anyValueRef); } else if ( leftValueType.kind === ValueTypeKind.NUMBER || rightValueType.kind === ValueTypeKind.NUMBER ) { const isLeftStatic = leftValueType.kind === ValueTypeKind.NUMBER ? true : false; res = operateF64F64ToDyn( module, leftValueRef, rightValueRef, opKind, isLeftStatic, !isLeftStatic, ); } else if ( leftValueType.kind === ValueTypeKind.STRING || rightValueType.kind === ValueTypeKind.STRING ) { const isLeftStatic = leftValueType.kind === ValueTypeKind.STRING ? true : false; res = operateStrStrToDyn( module, leftValueRef, rightValueRef, opKind, isLeftStatic, !isLeftStatic, ); } else { throw new UnimplementError( `operand type doesn't support on any static operation, static type is ${rightValueType}`, ); } if ( opKind === ts.SyntaxKind.ExclamationEqualsToken || opKind === ts.SyntaxKind.ExclamationEqualsEqualsToken ) { res = module.i32.eqz(res); } break; } default: if ( leftValueType.kind === ValueTypeKind.NUMBER || rightValueType.kind === ValueTypeKind.NUMBER ) { const isLeftStatic = leftValueType.kind === ValueTypeKind.NUMBER ? true : false; res = operateF64F64ToDyn( module, leftValueRef, rightValueRef, opKind, isLeftStatic, !isLeftStatic, ); } else if ( leftValueType.kind === ValueTypeKind.STRING || rightValueType.kind === ValueTypeKind.STRING ) { if (!UtilFuncs.isSupportedStringOP(opKind)) throw new UnimplementError( `operator doesn't support on any string operation, ${opKind}`, ); const isLeftStatic = leftValueType.kind === ValueTypeKind.STRING ? true : false; res = operateStrStrToDyn( module, leftValueRef, rightValueRef, opKind, isLeftStatic, !isLeftStatic, ); } else { throw new UnimplementError( `operator doesn't support on any static operation, ${opKind}`, ); } } return res; } export function judgeRealType( module: binaryen.Module, valueRef: binaryen.ExpressionRef, realType: ValueTypeKind, ) { const dynTypeCtx = getDynContextRef(module); let res = module.unreachable(); switch (realType) { case ValueTypeKind.STRING: { res = module.call( dyntype.dyntype_is_string, [dynTypeCtx, valueRef], binaryen.i32, ); break; } case ValueTypeKind.NUMBER: { res = module.call( dyntype.dyntype_is_number, [dynTypeCtx, valueRef], binaryen.i32, ); break; } } return res; } export function operateStaticToDyn( module: binaryen.Module, leftValueRef: binaryen.ExpressionRef, rightValueRef: binaryen.ExpressionRef, opKind: ts.SyntaxKind, ) { const needStringOp = module.select( judgeRealType(module, leftValueRef, ValueTypeKind.STRING), judgeRealType(module, leftValueRef, ValueTypeKind.STRING), judgeRealType(module, rightValueRef, ValueTypeKind.STRING), ); const needNumberOp = module.select( judgeRealType(module, leftValueRef, ValueTypeKind.NUMBER), judgeRealType(module, rightValueRef, ValueTypeKind.NUMBER), judgeRealType(module, leftValueRef, ValueTypeKind.NUMBER), ); const ifFalseRef = module.unreachable(); return module.if( needStringOp, operateStrStrToDyn(module, leftValueRef, rightValueRef, opKind), module.if( needNumberOp, operateF64F64ToDyn(module, leftValueRef, rightValueRef, opKind), ifFalseRef, ), ); } export function operateF64F64ToDyn( module: binaryen.Module, leftValueRef: binaryen.ExpressionRef, rightValueRef: binaryen.ExpressionRef, opKind: ts.SyntaxKind, isLeftStatic = false, isRightStatic = false, ) { const tmpLeftNumberRef = isLeftStatic ? leftValueRef : module.call( dyntype.dyntype_to_number, [getDynContextRef(module), leftValueRef], binaryen.f64, ); const tmpRightNumberRef = isRightStatic ? rightValueRef : module.call( dyntype.dyntype_to_number, [getDynContextRef(module), rightValueRef], binaryen.f64, ); const operateNumber = operateF64F64( module, tmpLeftNumberRef, tmpRightNumberRef, opKind, ); if (binaryen.getExpressionType(operateNumber) === binaryen.i32) { return operateNumber; } else { return generateDynNumber(module, operateNumber); } } export function operateStrStrToDyn( module: binaryen.Module, leftValueRef: binaryen.ExpressionRef, rightValueRef: binaryen.ExpressionRef, opKind: ts.SyntaxKind, isLeftStatic = false, isRightStatic = false, ) { const tmpLeftStrRef = isLeftStatic ? leftValueRef : unboxAnyToBase(module, leftValueRef, ValueTypeKind.STRING); const tmpRightStrRef = isRightStatic ? rightValueRef : unboxAnyToBase(module, rightValueRef, ValueTypeKind.STRING); let operateStringRef = module.unreachable(); if (UtilFuncs.isSupportedStringOP(opKind)) { operateStringRef = generateDynString( module, operateStringString( module, tmpLeftStrRef, tmpRightStrRef, opKind, ), ); } return operateStringRef; } export function oprateF64F64ToDyn( module: binaryen.Module, leftNumberExpression: binaryen.ExpressionRef, rightNumberExpression: binaryen.ExpressionRef, operatorKind: ts.SyntaxKind, ) { // operate left expression and right expression const operateTotalNumber = operateF64F64( module, leftNumberExpression, rightNumberExpression, operatorKind, ); // generate dynamic number if ( operatorKind === ts.SyntaxKind.GreaterThanToken || operatorKind === ts.SyntaxKind.GreaterThanEqualsToken || operatorKind === ts.SyntaxKind.LessThanToken || operatorKind === ts.SyntaxKind.LessThanEqualsToken || operatorKind === ts.SyntaxKind.EqualsEqualsToken || operatorKind === ts.SyntaxKind.EqualsEqualsEqualsToken || operatorKind === ts.SyntaxKind.ExclamationEqualsToken || operatorKind === ts.SyntaxKind.ExclamationEqualsEqualsToken ) { return operateTotalNumber; } return generateDynNumber(module, operateTotalNumber); } export function getArrayRefLen( module: binaryen.Module, arrStructRef: binaryen.ExpressionRef, arrayValue?: SemanticsValue, propLenRef?: binaryen.ExpressionRef, returnI32?: boolean, ): binaryen.ExpressionRef { let arrLenI32Ref: binaryen.ExpressionRef | undefined; if (!arrayValue || arrayValue.type.kind == ValueTypeKind.ARRAY) { arrLenI32Ref = binaryenCAPI._BinaryenStructGet( module.ptr, 1, arrStructRef, binaryen.getExpressionType(arrStructRef), false, ); } else if (arrayValue.type.kind == ValueTypeKind.ANY && propLenRef) { const anyArrRef = arrStructRef; arrLenI32Ref = module.i32.trunc_u.f64( unboxAnyToBase( module, getDynObjProp(module, anyArrRef, propLenRef), ValueTypeKind.NUMBER, ), ); } else if (arrayValue.type.kind === ValueTypeKind.WASM_ARRAY) { arrLenI32Ref = binaryenCAPI._BinaryenArrayLen( module.ptr, arrStructRef, ); } if (returnI32) { return arrLenI32Ref!; } else { return convertTypeToF64( module, arrLenI32Ref!, binaryen.getExpressionType(arrLenI32Ref!), ); } } export function getStringRefLen( module: binaryen.Module, stringRef: binaryen.ExpressionRef, ): binaryen.ExpressionRef { let strLenI32: binaryen.ExpressionRef; if (getConfig().enableStringRef) { strLenI32 = binaryenCAPI._BinaryenStringMeasure( module.ptr, StringRefMeatureOp.WTF16, stringRef, ); } else { const strArray = binaryenCAPI._BinaryenStructGet( module.ptr, 1, stringRef, i8ArrayTypeInfo.typeRef, false, ); strLenI32 = binaryenCAPI._BinaryenArrayLen(module.ptr, strArray); } const strLenF64 = convertTypeToF64( module, strLenI32, binaryen.getExpressionType(strLenI32), ); return strLenF64; } export function setArrayElemByIdx( module: binaryen.Module, ownerRef: binaryen.ExpressionRef, ownerHeapTypeRef: binaryenCAPI.HeapTypeRef, idxRef: binaryen.ExpressionRef, targetValueRef: binaryen.ExpressionRef, isRawArray = false, ) { let arrayOriRef: binaryen.ExpressionRef; if (isRawArray) { arrayOriRef = ownerRef; } else { arrayOriRef = binaryenCAPI._BinaryenStructGet( module.ptr, 0, ownerRef, ownerHeapTypeRef, false, ); } return binaryenCAPI._BinaryenArraySet( module.ptr, arrayOriRef, idxRef, targetValueRef, ); } export function getArrayElemByIdx( module: binaryen.Module, ownerTypeRef: binaryen.Type, ownerRef: binaryen.ExpressionRef, ownerHeapTypeRef: binaryenCAPI.HeapTypeRef, idxRef: binaryen.ExpressionRef, isRawArray = false, ) { let arrayOriRef: binaryen.ExpressionRef; if (isRawArray) { arrayOriRef = ownerRef; } else { arrayOriRef = binaryenCAPI._BinaryenStructGet( module.ptr, 0, ownerRef, ownerHeapTypeRef, false, ); } return binaryenCAPI._BinaryenArrayGet( module.ptr, arrayOriRef, idxRef, ownerTypeRef, false, ); } export function getFieldFromMetaByOffset( module: binaryen.Module, meta: binaryen.ExpressionRef, offset: number, ) { return module.i32.load(offset, memoryAlignment, meta); } export function getWasmStructFieldByIndex( module: binaryen.Module, ref: binaryen.ExpressionRef, typeRef: binaryen.Type, idx: number, ) { return binaryenCAPI._BinaryenStructGet( module.ptr, idx, ref, typeRef, false, ); } export function getWASMObjectVtable( module: binaryen.Module, ref: binaryen.ExpressionRef, ) { return getWasmStructFieldByIndex( module, ref, baseVtableType.typeRef, StructFieldIndex.VTABLE_INDEX, ); } export function getWASMObjectMeta( module: binaryen.Module, ref: binaryen.ExpressionRef, ) { const vtable = getWASMObjectVtable(module, ref); return getWasmStructFieldByIndex( module, vtable, binaryen.i32, VtableFieldIndex.META_INDEX, ); } export function getPropFlagFromObj( module: binaryen.Module, flagAndIndexRef: binaryen.ExpressionRef, ) { const flagRef = module.i32.and(flagAndIndexRef, module.i32.const(15)); return flagRef; } export function getPropIndexFromObj( module: binaryen.Module, flagAndIndexRef: binaryen.ExpressionRef, ) { const indexRef = module.i32.shr_u(flagAndIndexRef, module.i32.const(4)); return indexRef; } export function isPropertyUnExist( module: binaryen.Module, flagAndIndexRef: binaryen.ExpressionRef, ) { return module.i32.eq(flagAndIndexRef, module.i32.const(-1)); } export function isFieldFlag( module: binaryen.Module, flagRef: binaryen.ExpressionRef, ) { return module.i32.eq(flagRef, module.i32.const(ItableFlag.FIELD)); } export function isMethodFlag( module: binaryen.Module, flagRef: binaryen.ExpressionRef, ) { return module.i32.eq(flagRef, module.i32.const(ItableFlag.METHOD)); } export function isShapeCompatible( module: binaryen.Module, typeId: number, metaRef: binaryen.ExpressionRef, ) { /* judge if type_id is equal or impl_id is equal */ const infcTypeIdRef = module.i32.const(typeId); const objTypeIdRef = getFieldFromMetaByOffset( module, metaRef, MetaDataOffset.TYPE_ID_OFFSET, ); const objImplIdRef = getFieldFromMetaByOffset( module, metaRef, MetaDataOffset.IMPL_ID_OFFSET, ); const ifShapeCompatibal = module.i32.or( module.i32.eq(infcTypeIdRef, objTypeIdRef), module.i32.eq(infcTypeIdRef, objImplIdRef), ); return ifShapeCompatibal; } export function getPredefinedTypeId(type: ValueType): PredefinedTypeId { switch (type.kind) { case ValueTypeKind.UNDEFINED: case ValueTypeKind.UNION: case ValueTypeKind.TYPE_PARAMETER: case ValueTypeKind.ANY: { return PredefinedTypeId.ANY; } case ValueTypeKind.ENUM: { return getPredefinedTypeId((type).memberType); } case ValueTypeKind.NULL: return PredefinedTypeId.NULL; case ValueTypeKind.INT: return PredefinedTypeId.INT; case ValueTypeKind.NUMBER: return PredefinedTypeId.NUMBER; case ValueTypeKind.BOOLEAN: return PredefinedTypeId.BOOLEAN; case ValueTypeKind.RAW_STRING: case ValueTypeKind.STRING: return PredefinedTypeId.STRING; case ValueTypeKind.FUNCTION: return PredefinedTypeId.FUNCTION; case ValueTypeKind.ARRAY: return PredefinedTypeId.ARRAY; case ValueTypeKind.INTERFACE: case ValueTypeKind.OBJECT: { const objType = type as ObjectType; return objType.typeId; } case ValueTypeKind.WASM_I64: return PredefinedTypeId.WASM_I64; case ValueTypeKind.WASM_F32: return PredefinedTypeId.WASM_F32; case ValueTypeKind.TUPLE: return PredefinedTypeId.TUPLE; case ValueTypeKind.WASM_ARRAY: return PredefinedTypeId.WASM_ARRAY; case ValueTypeKind.WASM_STRUCT: return PredefinedTypeId.WASM_STRUCT; default: throw new UnimplementError( `encounter type not assigned type id, type kind is ${type.kind}`, ); } } export function isPropTypeIdEqual( module: binaryen.Module, propTypeIdRefFromType: binaryen.ExpressionRef, propTypeIdRefFromReal: binaryen.ExpressionRef, ) { return module.i32.eq(propTypeIdRefFromType, propTypeIdRefFromReal); } export function isPropTypeIdIsObject( module: binaryen.Module, propTypeIdRef: binaryen.ExpressionRef, ) { return module.i32.ge_u( propTypeIdRef, module.i32.const(PredefinedTypeId.CUSTOM_TYPE_BEGIN), ); } export function isPropTypeIdIsFunction( module: binaryen.Module, propTypeIdRef: binaryen.ExpressionRef, ) { return module.i32.eq( propTypeIdRef, module.i32.const(PredefinedTypeId.FUNCTION), ); } export function isPropTypeIdIsNullable( module: binaryen.Module, propTypeIdRef: binaryen.ExpressionRef, ) { return module.i32.or( isPropTypeIdIsFunction(module, propTypeIdRef), isPropTypeIdIsObject(module, propTypeIdRef), ); } export function isPropTypeIdCompatible( module: binaryen.Module, propTypeIdRefFromType: binaryen.ExpressionRef, propTypeIdRefFromReal: binaryen.ExpressionRef, ) { /* * If propType from type is A | null, it will be regarded as object type, not union type. * If propType from real is null, it will be regarded as empty type, we treat these two types as compatibal. */ const realIsNull = module.i32.and( isPropTypeIdIsNullable(module, propTypeIdRefFromType), module.i32.eq( propTypeIdRefFromReal, module.i32.const(PredefinedTypeId.NULL), ), ); const ifPropTypeIdCompatible = module.i32.or( isPropTypeIdEqual( module, propTypeIdRefFromType, propTypeIdRefFromReal, ), realIsNull, ); return ifPropTypeIdCompatible; } export function copyStringToLinearMemory( module: binaryen.Module, stringRef: binaryen.ExpressionRef, startIdx: number, calledParamValueRefs: binaryen.ExpressionRef[], vars: binaryen.Type[], mallocOffsets: binaryen.ExpressionRef[], ) { const stmts: binaryen.ExpressionRef[] = []; /* measure str length */ const propStrLen = binaryenCAPI._BinaryenStringMeasure( module.ptr, StringRefMeatureOp.UTF8, stringRef, ); /* malloc linear memory */ const targetOffset = module.call( BuiltinNames.mallocFunc, [propStrLen], binaryen.i32, ); const targetOffset_Idx = startIdx++; vars.push(binaryen.i32); mallocOffsets.push(module.local.get(targetOffset_Idx, binaryen.i32)); stmts.push(module.local.set(targetOffset_Idx, targetOffset)); /* encode string to linear memory */ const codeunits = binaryenCAPI._BinaryenStringEncode( module.ptr, StringRefMeatureOp.WTF8, stringRef, module.local.get(targetOffset_Idx, binaryen.i32), 0, ); /* add end to memory */ stmts.push( module.i32.store( 0, 4, module.i32.add( module.local.get(targetOffset_Idx, binaryen.i32), codeunits, ), module.i32.const(0), ), ); calledParamValueRefs.push( module.local.get(targetOffset_Idx, binaryen.i32), ); return module.block(null, stmts); } export function copyArrayBufferToLinearMemory( module: binaryen.Module, arrayBufferRef: binaryen.ExpressionRef, startIdx: number, calledParamValueRefs: binaryen.ExpressionRef[], vars: binaryen.Type[], mallocOffsets: binaryen.ExpressionRef[], ) { const stmts: binaryen.ExpressionRef[] = []; const i_Idx = startIdx++; vars.push(binaryen.i32); stmts.push(module.local.set(i_Idx, module.i32.const(0))); const loopIndexValue = module.local.get(i_Idx, binaryen.i32); const codesArray = binaryenCAPI._BinaryenStructGet( module.ptr, 0, arrayBufferRef, arrayBufferTypeInfo.typeRef, false, ); const codeLen = binaryenCAPI._BinaryenStructGet( module.ptr, 1, arrayBufferRef, arrayBufferTypeInfo.typeRef, false, ); /* malloc linear memory */ const targetOffset = module.call( BuiltinNames.mallocFunc, [codeLen], binaryen.i32, ); const targetOffset_Idx = startIdx++; mallocOffsets.push(module.local.get(targetOffset_Idx, binaryen.i32)); vars.push(binaryen.i32); stmts.push(module.local.set(targetOffset_Idx, targetOffset)); /* Put elem in linear memory */ const loopLabel = 'for_label'; const loopCond = module.i32.lt_s(loopIndexValue, codeLen); const loopIncrementor = module.local.set( i_Idx, module.i32.add(loopIndexValue, module.i32.const(1)), ); const loopBody: binaryen.ExpressionRef[] = []; loopBody.push( module.i32.store8( 0, 1, module.i32.add( module.local.get(targetOffset_Idx, binaryen.i32), loopIndexValue, ), binaryenCAPI._BinaryenArrayGet( module.ptr, codesArray, loopIndexValue, arrayBufferTypeInfo.typeRef, false, ), ), ); const flattenLoop: FlattenLoop = { label: loopLabel, condition: loopCond, statements: module.block(null, loopBody), incrementor: loopIncrementor, }; stmts.push( module.loop( loopLabel, FunctionalFuncs.flattenLoopStatement( module, flattenLoop, SemanticsKind.FOR, ), ), ); calledParamValueRefs.push( module.local.get(targetOffset_Idx, binaryen.i32), ); return module.block(null, stmts); } export function copyLinearMemoryToArrayBuffer( module: binaryen.Module, offsetValueRef: binaryen.ExpressionRef, lengthRef: binaryen.ExpressionRef, startIdx: number, calledParamValueRefs: binaryen.ExpressionRef[], vars: binaryen.Type[], ) { const stmts: binaryen.ExpressionRef[] = []; const i8Array = binaryenCAPI._BinaryenArrayNew( module.ptr, i8ArrayTypeInfo.heapTypeRef, lengthRef, module.i32.const(0), ); const arrayBufferRef = binaryenCAPI._BinaryenStructNew( module.ptr, arrayToPtr([i8Array, lengthRef]).ptr, 2, arrayBufferTypeInfo.heapTypeRef, ); const arrayBufferIdx = startIdx++; stmts.push(module.local.set(arrayBufferIdx, arrayBufferRef)); vars.push(arrayBufferTypeInfo.typeRef); calledParamValueRefs.push( module.local.get(arrayBufferIdx, arrayBufferTypeInfo.typeRef), ); const i_Idx = startIdx++; stmts.push(module.local.set(i_Idx, module.i32.const(0))); vars.push(binaryen.i32); const loopIndexValue = module.local.get(i_Idx, binaryen.i32); const codesArray = binaryenCAPI._BinaryenStructGet( module.ptr, 0, module.local.get(arrayBufferIdx, arrayBufferTypeInfo.typeRef), arrayBufferTypeInfo.typeRef, false, ); /* Put elem in arraybuffer */ const loopLabel = 'for_label'; const loopCond = module.i32.lt_s(loopIndexValue, lengthRef); const loopIncrementor = module.local.set( i_Idx, module.i32.add(loopIndexValue, module.i32.const(1)), ); const loopBody: binaryen.ExpressionRef[] = []; loopBody.push( binaryenCAPI._BinaryenArraySet( module.ptr, codesArray, loopIndexValue, module.i32.load8_s( 0, 1, module.i32.add(offsetValueRef, loopIndexValue), ), ), ); const flattenLoop: FlattenLoop = { label: loopLabel, condition: loopCond, statements: module.block(null, loopBody), incrementor: loopIncrementor, }; stmts.push( module.loop( loopLabel, FunctionalFuncs.flattenLoopStatement( module, flattenLoop, SemanticsKind.FOR, ), ), ); return module.block(null, stmts); } export function generateConvertRule( fromType: ValueType, toType: ValueType, ) { if (fromType.kind === ValueTypeKind.OBJECT) { const className = (fromType as ObjectType).meta.name; if (className === BuiltinNames.ARRAYBUFFER) { if (toType.kind === ValueTypeKind.INT) { return NativeSignatureConversion.ARRAYBUFFER_TO_I32; } } } else if (fromType.kind === ValueTypeKind.INT) { if (toType.kind === ValueTypeKind.OBJECT) { const className = (toType as ObjectType).meta.name; if (className === BuiltinNames.ARRAYBUFFER) { return NativeSignatureConversion.I32_TO_ARRAYBUFFER; } } else if (toType.kind === ValueTypeKind.INT) { return NativeSignatureConversion.I32_TO_I32; } } else if (fromType.kind === ValueTypeKind.STRING) { if (toType.kind === ValueTypeKind.STRING) { return NativeSignatureConversion.STRING_TO_STRING; } else if (toType.kind === ValueTypeKind.INT) { return NativeSignatureConversion.STRING_TO_I32; } } return NativeSignatureConversion.INVALID; } export function parseNativeSignatureConversion( fromTypes: ValueType[], toTypes: ValueType[], ) { /* fromTypes is the wrapper functions' parameter types, toTypes is the real functions's parameter types */ if (fromTypes.length !== toTypes.length) { throw new Error( `NativeSignature's parameter length must match real function's parameter length`, ); } const convertRules: NativeSignatureConversion[] = []; for (let i = 0; i < fromTypes.length; i++) { convertRules.push(generateConvertRule(fromTypes[i], toTypes[i])); } return convertRules; } export function parseNativeSignature( module: binaryen.Module, innerOpStmts: binaryen.ExpressionRef[], fromTypes: ValueType[], fromTypeRefs: binaryen.Type[], toTypes: ValueType[], skipEnvParamLen: number, calledParamValueRefs: binaryen.ExpressionRef[], vars: binaryen.Type[], mallocOffsets: binaryen.ExpressionRef[], isImport: boolean, ) { const convertRules = FunctionalFuncs.parseNativeSignatureConversion( fromTypes, toTypes, ); let tmpVarIdx = isImport ? fromTypes.length + skipEnvParamLen : fromTypes.length; for (let i = 0; i < convertRules.length; i++) { const fromRef = module.local.get( isImport ? i + skipEnvParamLen : i, fromTypeRefs[i], ); const varsStartLen = vars.length; switch (convertRules[i]) { case NativeSignatureConversion.ARRAYBUFFER_TO_I32: { innerOpStmts.push( copyArrayBufferToLinearMemory( module, fromRef, tmpVarIdx, calledParamValueRefs, vars, mallocOffsets, ), ); break; } case NativeSignatureConversion.I32_TO_ARRAYBUFFER: { assert(i + 1 < convertRules.length, `${i + 1} must exsit`); const lengthRef = module.local.get( isImport ? i + skipEnvParamLen + 1 : i + 1, fromTypeRefs[i + 1], ); innerOpStmts.push( copyLinearMemoryToArrayBuffer( module, fromRef, lengthRef, tmpVarIdx, calledParamValueRefs, vars, ), ); break; } case NativeSignatureConversion.STRING_TO_I32: { innerOpStmts.push( copyStringToLinearMemory( module, fromRef, tmpVarIdx, calledParamValueRefs, vars, mallocOffsets, ), ); break; } case NativeSignatureConversion.STRING_TO_STRING: case NativeSignatureConversion.I32_TO_I32: { calledParamValueRefs.push(fromRef); break; } case NativeSignatureConversion.INVALID: { throw new Error( 'nativeSignature conversion rule is invalid', ); } default: { throw new Error('not implemented yet'); } } tmpVarIdx += vars.length - varsStartLen; } } } ================================================ FILE: src/backend/binaryen/wasm_expr_gen.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import ts from 'typescript'; import binaryen from 'binaryen'; import * as binaryenCAPI from './glue/binaryen.js'; import { builtinClosureType, arrayToPtr, emptyStructType, generateArrayStructTypeInfo, StringRefMeatureOp, baseStructType, } from './glue/transform.js'; import { assert } from 'console'; import { WASMGen } from './index.js'; import { Logger } from '../../log.js'; import { UtilFuncs, FunctionalFuncs, ItableFlag, FlattenLoop, MetaDataOffset, BackendLocalVar, } from './utils.js'; import { PredefinedTypeId, getUtilsFuncName, processEscape, } from '../../utils.js'; import { BinaryExprValue, BlockBranchIfValue, BlockBranchValue, BlockValue, CastValue, ClosureCallValue, ConditionExprValue, DirectCallValue, DirectGetValue, DirectGetterValue, DirectSetterValue, DynamicCallValue, DynamicGetValue, DynamicSetValue, ElementGetValue, ElementSetValue, FunctionCallValue, LiteralValue, NewArrayLenValue, NewArrayValue, NewClosureFunction, NewLiteralArrayValue, NewLiteralObjectValue, OffsetGetValue, OffsetSetValue, PostUnaryExprValue, PrefixUnaryExprValue, SemanticsValue, SemanticsValueKind, ShapeCallValue, ShapeGetValue, ShapeSetValue, SuperValue, VarValue, OffsetCallValue, VTableCallValue, TypeofValue, ToStringValue, AnyCallValue, SuperUsageFlag, CommaExprValue, ReBindingValue, SpreadValue, TemplateExprValue, EnumerateKeysGetValue, VTableGetValue, VTableSetValue, } from '../../semantics/value.js'; import { ArrayType, ClosureContextType, FunctionType, ObjectType, ObjectTypeFlag, Primitive, PrimitiveType, TupleType, TypeParameterType, UnionType, ValueType, ValueTypeKind, ValueTypeWithArguments, WASMArrayType, WASMStructType, } from '../../semantics/value_types.js'; import { UnimplementError } from '../../error.js'; import { FunctionDeclareNode, SemanticsKind, VarDeclareNode, } from '../../semantics/semantics_nodes.js'; import { MemberDescription, MemberType, ObjectDescription, ObjectDescriptionType, } from '../../semantics/runtime.js'; import { NewConstructorObjectValue } from '../../semantics/value.js'; import { BuiltinNames } from '../../../lib/builtin/builtin_name.js'; import { dyntype, structdyn } from './lib/dyntype/utils.js'; import { stringArrayStructTypeInfo, stringrefArrayStructTypeInfo, stringArrayTypeInfo, stringrefArrayTypeInfo, i32ArrayTypeInfo, } from './glue/packType.js'; import { getBuiltInFuncName } from '../../utils.js'; import { stringTypeInfo } from './glue/packType.js'; import { getConfig } from '../../../config/config_mgr.js'; import { GetBuiltinObjectType } from '../../semantics/builtin.js'; export class WASMExpressionGen { private module: binaryen.Module; private wasmTypeGen; constructor(private wasmCompiler: WASMGen) { this.module = this.wasmCompiler.module; this.wasmTypeGen = this.wasmCompiler.wasmTypeComp; } wasmExprGen(value: SemanticsValue): binaryen.ExpressionRef { this.module = this.wasmCompiler.module; this.wasmTypeGen = this.wasmCompiler.wasmTypeComp; switch (value.kind) { case SemanticsValueKind.SUPER: return this.wasmSuper(value); case SemanticsValueKind.LITERAL: return this.wasmLiteral(value); case SemanticsValueKind.PARAM_VAR: case SemanticsValueKind.LOCAL_VAR: case SemanticsValueKind.LOCAL_CONST: case SemanticsValueKind.GLOBAL_VAR: case SemanticsValueKind.GLOBAL_CONST: case SemanticsValueKind.CLOSURE_VAR: case SemanticsValueKind.CLOSURE_CONST: return this.wasmGetValue(value); case SemanticsValueKind.NEW_CLOSURE_FUNCTION: return this.wasmGetClosure(value); case SemanticsValueKind.BINARY_EXPR: return this.wasmBinaryExpr(value); case SemanticsValueKind.COMMA_EXPR: return this.wasmCommaExpr(value); case SemanticsValueKind.POST_UNARY_EXPR: return this.wasmPostUnaryExpr(value); case SemanticsValueKind.PRE_UNARY_EXPR: return this.wasmPreUnaryExpr(value); case SemanticsValueKind.CONDITION_EXPR: return this.wasmConditionalExpr(value); case SemanticsValueKind.OFFSET_CALL: return this.wasmOffsetCall(value); case SemanticsValueKind.DIRECT_CALL: return this.wasmDirectCall(value); case SemanticsValueKind.FUNCTION_CALL: return this.wasmFunctionCall(value); case SemanticsValueKind.ENUMERATE_KEY_GET: return this.wasmEnumerateKeysGet(value); case SemanticsValueKind.CLOSURE_CALL: return this.wasmClosureCall(value); case SemanticsValueKind.DYNAMIC_CALL: return this.wasmDynamicCall(value); case SemanticsValueKind.VTABLE_CALL: return this.wasmVtableCall(value); case SemanticsValueKind.ANY_CALL: return this.wasmAnyCall(value); case SemanticsValueKind.ANY_CAST_VALUE: case SemanticsValueKind.VALUE_CAST_ANY: case SemanticsValueKind.VALUE_CAST_UNION: case SemanticsValueKind.UNION_CAST_VALUE: case SemanticsValueKind.OBJECT_CAST_ANY: case SemanticsValueKind.OBJECT_CAST_UNION: case SemanticsValueKind.UNION_CAST_OBJECT: case SemanticsValueKind.UNION_CAST_ANY: case SemanticsValueKind.ANY_CAST_OBJECT: case SemanticsValueKind.OBJECT_CAST_VALUE: return this.wasmAnyCast(value); case SemanticsValueKind.VALUE_CAST_VALUE: return this.wasmValueCast(value); case SemanticsValueKind.SHAPE_SET: return this.wasmObjFieldSet(value); case SemanticsValueKind.OFFSET_SET: return this.wasmObjFieldSet(value); case SemanticsValueKind.VTABLE_SET: return this.wasmObjFieldSet(value); case SemanticsValueKind.NEW_LITERAL_OBJECT: return this.wasmNewLiteralObj(value); case SemanticsValueKind.OBJECT_CAST_OBJECT: return this.wasmObjCast(value); case SemanticsValueKind.NEW_CONSTRCTOR_OBJECT: return this.wasmNewClass(value); case SemanticsValueKind.SHAPE_GET: return this.wasmObjFieldGet(value); case SemanticsValueKind.DIRECT_GET: return this.wasmObjFieldGet(value); case SemanticsValueKind.OFFSET_GETTER: case SemanticsValueKind.OFFSET_GET: return this.wasmObjFieldGet(value); case SemanticsValueKind.VTABLE_GET: return this.wasmObjFieldGet(value); case SemanticsValueKind.DYNAMIC_GET: return this.wasmDynamicGet(value); case SemanticsValueKind.DYNAMIC_SET: return this.wasmDynamicSet(value); case SemanticsValueKind.NEW_LITERAL_ARRAY: return this.wasmNewLiteralArray(value); case SemanticsValueKind.ARRAY_INDEX_GET: case SemanticsValueKind.OBJECT_KEY_GET: case SemanticsValueKind.STRING_INDEX_GET: case SemanticsValueKind.TUPLE_INDEX_GET: case SemanticsValueKind.WASMARRAY_INDEX_GET: case SemanticsValueKind.WASMSTRUCT_INDEX_GET: return this.wasmElemGet(value); case SemanticsValueKind.ARRAY_INDEX_SET: case SemanticsValueKind.OBJECT_KEY_SET: case SemanticsValueKind.STRING_INDEX_SET: case SemanticsValueKind.TUPLE_INDEX_SET: case SemanticsValueKind.WASMARRAY_INDEX_SET: case SemanticsValueKind.WASMSTRUCT_INDEX_SET: return this.wasmElemSet(value); case SemanticsValueKind.BLOCK: return this.wasmBlockValue(value); case SemanticsValueKind.BLOCK_BRANCH_IF: return this.wasmBlockIFValue(value); case SemanticsValueKind.BLOCK_BRANCH: return this.wasmBlockBranchValue(value); case SemanticsValueKind.SHAPE_CALL: return this.wasmShapeCall(value); case SemanticsValueKind.DIRECT_GETTER: return this.wasmDirectGetter(value); case SemanticsValueKind.DIRECT_SETTER: return this.wasmDirectSetter(value); case SemanticsValueKind.NEW_ARRAY: case SemanticsValueKind.NEW_ARRAY_LEN: return this.wasmNewArray( value, ); case SemanticsValueKind.TYPEOF: return this.wasmTypeof(value); case SemanticsValueKind.TEMPLATE_EXPRESSION: return this.wasmTemplateExpr(value); case SemanticsValueKind.VALUE_TO_STRING: case SemanticsValueKind.OBJECT_TO_STRING: return this.wasmToString(value); case SemanticsValueKind.REBINDING: return this.wasmReBinding(value); case SemanticsValueKind.SPREAD: return this.wasmSpread(value); default: throw new UnimplementError(`unexpected value: ${value}`); } } private wasmSuper(value: SuperValue): binaryen.ExpressionRef { if (value.usageFlag == SuperUsageFlag.SUPER_CALL) { const constructor = value.shape?.meta.name + '|constructor'; const metaInfo = (value.type as ObjectType).meta; const ctorFuncDecl = ( metaInfo.ctor!.methodOrAccessor!.method! as VarValue ).ref as FunctionDeclareNode; const thisRef = this.module.local.get(1, emptyStructType.typeRef); return this.module.drop( this.callFunc( metaInfo.ctor!.valueType as FunctionType, constructor, binaryen.none, value.parameters, ctorFuncDecl, undefined, thisRef, ), ); } else { return this.module.local.get(1, emptyStructType.typeRef); } } private wasmLiteral(value: LiteralValue): binaryen.ExpressionRef { switch (value.type.kind) { case ValueTypeKind.NUMBER: { return this.module.f64.const(value.value as number); } case ValueTypeKind.BOOLEAN: { const literalValue = value.value as boolean; if (literalValue) { return this.module.i32.const(1); } else { return this.module.i32.const(0); } } case ValueTypeKind.RAW_STRING: case ValueTypeKind.STRING: { if (getConfig().enableStringRef) { return this.createStringRef(value.value as string); } else { return FunctionalFuncs.generateStringForStructArrayStr( this.module, value.value as string, ); } } case ValueTypeKind.NULL: { return this.module.ref.null( binaryenCAPI._BinaryenTypeStructref(), ); } case ValueTypeKind.UNDEFINED: { /* Currently, we treat undefined as any */ return FunctionalFuncs.generateDynUndefined(this.module); } case ValueTypeKind.INT: { return this.module.i32.const(value.value as number); } case ValueTypeKind.WASM_I64: { const val = value.value as number; return this.module.i64.const( val & 0xffffffff, (val / Math.pow(2, 32)) & 0xffffffff, ); } case ValueTypeKind.WASM_F32: { return this.module.f32.const(value.value as number); } default: { throw new UnimplementError(`TODO: wasmLiteral: ${value}`); } } } private createStringRef(value: string): binaryen.ExpressionRef { const ptr = this.wasmCompiler.generateRawString(value); const len = UtilFuncs.utf16ToUtf8(value).length; return FunctionalFuncs.generateStringForStringref( this.module, this.module.i32.const(ptr), this.module.i32.const(len), ); } private encodeStringrefToLinearMemory(stringref: binaryen.ExpressionRef) { const storeInMemoryStmts: binaryen.ExpressionRef[] = []; /* measure str length */ const propStrLen = binaryenCAPI._BinaryenStringMeasure( this.module.ptr, StringRefMeatureOp.UTF8, stringref, ); /* encode str to memory */ const memoryReserveOffsetRef = this.module.i32.const( BuiltinNames.memoryReserveOffset, ); const codeunits = binaryenCAPI._BinaryenStringEncode( this.module.ptr, StringRefMeatureOp.WTF8, stringref, memoryReserveOffsetRef, 0, ); /* add end to memory */ storeInMemoryStmts.push( this.module.i32.store( 0, 4, this.module.i32.add(memoryReserveOffsetRef, codeunits), this.module.i32.const(0), ), ); this.wasmCompiler.currentFuncCtx!.insert( this.module.if( this.module.i32.lt_s( propStrLen, this.module.i32.const(BuiltinNames.memoryReserveMaxSize), ), this.module.block(null, storeInMemoryStmts), this.module.unreachable(), ), ); return memoryReserveOffsetRef; } private getStringOffset(value: string): binaryen.ExpressionRef { return this.module.i32.const( this.wasmCompiler.generateRawString(value), ); } private wasmGetValue(value: VarValue): binaryen.ExpressionRef { const varNode = value.ref; const varTypeRef = this.wasmTypeGen.getWASMValueType(value.type); /** when meeting a ValueType as value, return wasm type */ if (value.ref instanceof ValueType) { return varTypeRef; } switch (value.kind) { case SemanticsValueKind.PARAM_VAR: case SemanticsValueKind.LOCAL_VAR: case SemanticsValueKind.LOCAL_CONST: { const varDeclNode = varNode as VarDeclareNode; if (varDeclNode.isUsedInClosureFunction()) { const currCtx = varDeclNode.curCtx; const belongCtx = varDeclNode.belongCtx; if (!currCtx || !belongCtx) { throw new Error( `get value failed in getting context of closure, varNode is ${varDeclNode.name}`, ); } let currCtxType = currCtx.type as ClosureContextType; const belongCtxType = belongCtx.type as ClosureContextType; let contextTypeRef = this.wasmTypeGen.getWASMType(currCtxType); let contextRef = this.module.local.get( currCtx.index, contextTypeRef, ); while (currCtxType != belongCtxType) { if (currCtxType.freeVarTypeList.length !== 0) { contextRef = binaryenCAPI._BinaryenStructGet( this.module.ptr, 0, contextRef, contextTypeRef, false, ); } currCtxType = currCtxType.parentCtxType!; contextTypeRef = this.wasmTypeGen.getWASMType(currCtxType); } return binaryenCAPI._BinaryenStructGet( this.module.ptr, varDeclNode!.closureIndex! + 1, contextRef, contextTypeRef, false, ); } else { return this.module.local.get(varNode.index, varTypeRef); } } case SemanticsValueKind.GLOBAL_VAR: case SemanticsValueKind.GLOBAL_CONST: { if (varNode instanceof VarDeclareNode) { if (varNode.name === BuiltinNames.nanName) { return this.module.f64.const(NaN); } else if (varNode.name === BuiltinNames.infinityName) { return this.module.f64.const(Infinity); } else if ( varNode.name.includes( BuiltinNames.builtinTypeManglePrefix, ) ) { const fallbackTypeName = varNode.name.substring( varNode.name.indexOf( BuiltinNames.builtinTypeManglePrefix, ), ); if ( !BuiltinNames.fallbackGlobalNames.includes( fallbackTypeName, ) ) { throw new UnimplementError( `type ${fallbackTypeName} doesn't exist in fallback type names`, ); } const origName = varNode.name.split( BuiltinNames.moduleDelimiter, )[1]; BuiltinNames.JSGlobalObjects.add(origName); return this.module.global.get( origName, binaryen.anyref, ); } return this.module.global.get(varNode.name, varTypeRef); } else if (varNode instanceof FunctionDeclareNode) { return this.createClosureStruct(varNode); } else { throw new UnimplementError( `need to handle global var in wasmGetVar: ${value}`, ); } } default: throw new UnimplementError( `Need to handle ${value.kind} in wasmGetVar`, ); } } private wasmGetClosure(value: NewClosureFunction): binaryen.ExpressionRef { return this.createClosureStruct(value.funcNode); } private createClosureStruct(funcNode: FunctionDeclareNode) { const funcTypeRef = this.wasmTypeGen.getWASMType(funcNode.funcType); const closureStructHeapTypeRef = this.wasmTypeGen.getWASMValueHeapType( funcNode.funcType, ); const closureContextRef = funcNode.parentCtx ? this.module.local.get( funcNode.parentCtx.index, this.wasmTypeGen.getWASMValueType(funcNode.parentCtx.type), ) : this.wasmCompiler.emptyRef; const closureStruct = binaryenCAPI._BinaryenStructNew( this.module.ptr, arrayToPtr([ closureContextRef, this.wasmCompiler.emptyRef, this.module.ref.func(funcNode.name, funcTypeRef), ]).ptr, 3, closureStructHeapTypeRef, ); return closureStruct; } private wasmSetValue( value: VarValue, targetValue: SemanticsValue, ): binaryen.ExpressionRef { const varNode = value.ref as VarDeclareNode; const targetValueRef = this.wasmExprGen(targetValue); switch (value.kind) { case SemanticsValueKind.PARAM_VAR: case SemanticsValueKind.LOCAL_VAR: case SemanticsValueKind.LOCAL_CONST: { if (varNode.isUsedInClosureFunction()) { const currCtx = varNode.curCtx; const belongCtx = varNode.belongCtx; if (!currCtx || !belongCtx) { throw new Error( `set value failed in getting context of closure, varNode is ${varNode.name}`, ); } let currCtxType = currCtx.type as ClosureContextType; const belongCtxType = belongCtx.type as ClosureContextType; let contextTypeRef = this.wasmTypeGen.getWASMType(currCtxType); let contextRef = this.module.local.get( currCtx.index, contextTypeRef, ); while (currCtxType != belongCtxType) { if (currCtxType.freeVarTypeList.length !== 0) { contextRef = binaryenCAPI._BinaryenStructGet( this.module.ptr, 0, contextRef, contextTypeRef, false, ); } currCtxType = currCtxType.parentCtxType!; contextTypeRef = this.wasmTypeGen.getWASMType(currCtxType); } return binaryenCAPI._BinaryenStructSet( this.module.ptr, varNode.closureIndex! + 1, contextRef, targetValueRef, ); } else { return this.module.local.set(varNode.index, targetValueRef); } } case SemanticsValueKind.GLOBAL_VAR: case SemanticsValueKind.GLOBAL_CONST: return this.module.global.set(varNode.name, targetValueRef); default: throw new UnimplementError( `Need to handle ${value.kind} in wasmSetVar`, ); } } private wasmBinaryExpr(value: BinaryExprValue): binaryen.ExpressionRef { const opKind = value.opKind; const leftValue = value.left; const rightValue = value.right; switch (opKind) { case ts.SyntaxKind.EqualsToken: { return this.assignBinaryExpr(leftValue, rightValue); } case ts.SyntaxKind.InstanceOfKeyword: { return this.wasmInstanceOf(leftValue, rightValue); } default: { return this.operateBinaryExpr(leftValue, rightValue, opKind); } } } private wasmCommaExpr(value: CommaExprValue): binaryen.ExpressionRef { const exprs: binaryen.ExpressionRef[] = []; for (const expr of value.exprs) { exprs.push(this.wasmExprGen(expr)); } return this.module.block(null, exprs); } private wasmAnyGen(expr: SemanticsValue): binaryen.ExpressionRef { /* TODO */ return binaryen.unreachable; } operateBinaryExpr( leftValue: SemanticsValue, rightValue: SemanticsValue, opKind: ts.BinaryOperator, ): binaryen.ExpressionRef { const leftValueType = leftValue.type; const leftValueRef = this.wasmExprGen(leftValue); const leftRefType = binaryen.getExpressionType(leftValueRef); const rightValueType = rightValue.type; const rightValueRef = this.wasmExprGen(rightValue); const rightRefType = binaryen.getExpressionType(rightValueRef); if ( leftValueType.kind === ValueTypeKind.NUMBER && rightValueType.kind === ValueTypeKind.NUMBER ) { return FunctionalFuncs.operateF64F64( this.module, leftValueRef, rightValueRef, opKind, ); } if ( leftValueType.kind === ValueTypeKind.INT && rightValueType.kind === ValueTypeKind.INT ) { return FunctionalFuncs.operateI32I32( this.module, leftValueRef, rightValueRef, opKind, ); } if ( leftValueType.kind === ValueTypeKind.WASM_I64 && rightValueType.kind === ValueTypeKind.WASM_I64 ) { return FunctionalFuncs.operateI64I64( this.module, leftValueRef, rightValueRef, opKind, ); } if ( leftValueType.kind === ValueTypeKind.WASM_F32 && rightValueType.kind === ValueTypeKind.WASM_F32 ) { return FunctionalFuncs.operateF32F32( this.module, leftValueRef, rightValueRef, opKind, ); } if ( leftValueType.kind === ValueTypeKind.NUMBER && (rightValueType.kind === ValueTypeKind.BOOLEAN || rightValueType.kind === ValueTypeKind.INT) ) { return FunctionalFuncs.operateF64I32( this.module, leftValueRef, rightValueRef, opKind, ); } if ( leftValueType.kind === ValueTypeKind.BOOLEAN && rightValueType.kind === ValueTypeKind.NUMBER ) { return FunctionalFuncs.operateI32F64( this.module, leftValueRef, rightValueRef, opKind, ); } if ( leftValueType.kind === ValueTypeKind.BOOLEAN && rightValueType.kind === ValueTypeKind.BOOLEAN ) { return FunctionalFuncs.operateI32I32( this.module, leftValueRef, rightValueRef, opKind, ); } if ( FunctionalFuncs.treatAsAny(leftValueType.kind) && FunctionalFuncs.treatAsAny(rightValueType.kind) ) { /* any will be cast to real type when running, now only number is considered */ return FunctionalFuncs.operateAnyAny( this.module, leftValueRef, rightValueRef, opKind, ); } if ( (leftValueType.kind === ValueTypeKind.STRING || leftValueType.kind === ValueTypeKind.RAW_STRING) && (rightValueType.kind === ValueTypeKind.STRING || rightValueType.kind === ValueTypeKind.RAW_STRING) ) { return FunctionalFuncs.operateStringString( this.module, leftValueRef, rightValueRef, opKind, ); } if ( (leftValueType.kind === ValueTypeKind.NULL || leftValueType.kind === ValueTypeKind.UNDEFINED) && !FunctionalFuncs.treatAsAny(rightValueType.kind) ) { return FunctionalFuncs.operateStaticNullUndefined( this.module, rightValueType, rightValueRef, leftValueType.kind, opKind, ); } if ( (rightValueType.kind === ValueTypeKind.NULL || rightValueType.kind === ValueTypeKind.UNDEFINED) && !FunctionalFuncs.treatAsAny(leftValueType.kind) ) { return FunctionalFuncs.operateStaticNullUndefined( this.module, leftValueType, leftValueRef, rightValueType.kind, opKind, ); } /** static any*/ if ( (FunctionalFuncs.treatAsAny(leftValueType.kind) && !FunctionalFuncs.treatAsAny(rightValueType.kind)) || (!FunctionalFuncs.treatAsAny(leftValueType.kind) && FunctionalFuncs.treatAsAny(rightValueType.kind)) ) { return FunctionalFuncs.operatorAnyStatic( this.module, leftValueRef, leftValueType, rightValueRef, rightValueType, opKind, ); } // iff array, class or interface if ( (leftValueType.kind === ValueTypeKind.ARRAY && rightValueType.kind === ValueTypeKind.ARRAY) || (leftValueType instanceof ObjectType && rightValueType instanceof ObjectType) ) { return FunctionalFuncs.operateRefRef( this.module, leftValueRef, rightValueRef, opKind, ); } throw new UnimplementError( `unsupported operation between ${leftValueType} and ${rightValueType}`, ); } private assignBinaryExpr( leftValue: SemanticsValue, rightValue: SemanticsValue, ): binaryen.ExpressionRef { if (leftValue instanceof VarValue) { return this.wasmSetValue(leftValue, rightValue); } else if (leftValue instanceof ShapeSetValue) { return this.wasmObjFieldSet(leftValue, rightValue); } else if ( leftValue instanceof OffsetSetValue || leftValue instanceof OffsetGetValue ) { return this.wasmObjFieldSet(leftValue, rightValue); } else { throw new UnimplementError(`assignBinaryExpr ${leftValue}`); } } private wasmPostUnaryExpr( value: PostUnaryExprValue, ): binaryen.ExpressionRef { if (!value.flattenExprValue) { throw new UnimplementError(`wasmPostUnaryExpr: ${value.opKind}`); } const exprTypeRef = this.wasmTypeGen.getWASMValueType(value.type); const unaryOp = this.wasmExprGen( value.flattenExprValue as BinaryExprValue, ); const getValueOp = this.wasmExprGen(value.target); let getOriValueOp = binaryen.none; const opKind = value.opKind; switch (opKind) { case ts.SyntaxKind.PlusPlusToken: { if (value.type.kind === ValueTypeKind.NUMBER) { getOriValueOp = this.module.f64.sub( getValueOp, this.module.f64.const(1), ); } else if (value.type.kind === ValueTypeKind.INT) { getOriValueOp = this.module.i32.sub( getValueOp, this.module.i32.const(1), ); } else if (value.type.kind === ValueTypeKind.WASM_F32) { getOriValueOp = this.module.f32.sub( getValueOp, this.module.f32.const(1), ); } else if (value.type.kind === ValueTypeKind.WASM_I64) { getOriValueOp = this.module.i64.sub( getValueOp, this.module.i64.const(1, 0), ); } break; } case ts.SyntaxKind.MinusMinusToken: { if (value.type.kind === ValueTypeKind.NUMBER) { getOriValueOp = this.module.f64.add( getValueOp, this.module.f64.const(1), ); } else if (value.type.kind === ValueTypeKind.INT) { getOriValueOp = this.module.i32.add( getValueOp, this.module.i32.const(1), ); } else if (value.type.kind === ValueTypeKind.WASM_F32) { getOriValueOp = this.module.f32.add( getValueOp, this.module.f32.const(1), ); } else if (value.type.kind === ValueTypeKind.WASM_I64) { getOriValueOp = this.module.i64.add( getValueOp, this.module.i64.const(1, 0), ); } break; } } return this.module.block(null, [unaryOp, getOriValueOp], exprTypeRef); } private wasmPreUnaryExpr( value: PrefixUnaryExprValue, ): binaryen.ExpressionRef { const opKind = value.opKind; switch (opKind) { case ts.SyntaxKind.PlusPlusToken: case ts.SyntaxKind.MinusMinusToken: { if (!value.flattenExprValue) { throw new UnimplementError( `wasmPreUnaryExpr: ${value.opKind}`, ); } const exprTypeRef = this.wasmTypeGen.getWASMValueType( value.type, ); const unaryOp = this.wasmExprGen( value.flattenExprValue as BinaryExprValue, ); const getValueOp = this.wasmExprGen(value.target); return this.module.block( null, [unaryOp, getValueOp], exprTypeRef, ); } case ts.SyntaxKind.ExclamationToken: { const operandValueRef = this.wasmExprGen(value.target); let result = FunctionalFuncs.generateCondition( this.module, operandValueRef, value.type.kind, ); result = this.module.i32.eqz(result); if (value.type.kind === ValueTypeKind.NUMBER) { /* Workaround: semantic tree treat result of !number as number, so we convert it back to number */ result = this.module.f64.convert_u.i32(result); } return result; } case ts.SyntaxKind.MinusToken: { if (!value.flattenExprValue) { throw new UnimplementError( `wasmPreUnaryExpr: ${value.opKind}`, ); } return this.wasmExprGen(value.flattenExprValue); } case ts.SyntaxKind.PlusToken: { return this.wasmExprGen(value.target); } default: throw new UnimplementError('wasmPreUnaryExpr: ${opKind}'); } } private wasmConditionalExpr( value: ConditionExprValue, ): binaryen.ExpressionRef { let condValueRef = this.wasmExprGen(value.condition); /* convert to condition */ condValueRef = FunctionalFuncs.generateCondition( this.module, condValueRef, value.condition.type.kind, ); const trueValueRef = this.wasmExprGen(value.trueExpr); const falseValueRef = this.wasmExprGen(value.falseExpr); assert( binaryen.getExpressionType(trueValueRef) === binaryen.getExpressionType(falseValueRef), 'trueWASMExprType and falseWASMExprType are not equal in conditional expression ', ); return this.module.select(condValueRef, trueValueRef, falseValueRef); } private wasmInstanceOf( leftValue: SemanticsValue, rightValue: SemanticsValue, ) { const leftValueType = leftValue.type; const rightValueType = rightValue.type; if (!(rightValueType instanceof ObjectType)) { // Only support instanceof right-side is an ObjectType throw new Error('wasmInstanceOf: rightValue is not ObjectType'); } if (!rightValueType.instanceType) { throw new Error( 'wasmInstanceOf: rightValue does not have ObjectType', ); } const rightValueInstType = (rightValueType as ObjectType).instanceType!; /** try to determine the result in compile time */ if (leftValueType instanceof ObjectType) { let type: ObjectType | undefined = leftValueType; while (type) { if (type.equals(rightValueInstType)) { return this.module.i32.const(1); } type = type.super; } } if ( leftValueType instanceof ObjectType && rightValueInstType instanceof ObjectType ) { let type: ObjectType | undefined = rightValueInstType; let isInInheritanceChain = false; while (type) { if (type.equals(leftValueType)) { isInInheritanceChain = true; } type = type.super; } /* if left-side is object, if right-side is not interface, and not in inheritanceChain, return false */ if ( !isInInheritanceChain && !leftValueType.meta.isInterface && !rightValueType.meta.isInterface ) { return this.module.i32.const(0); } } /** try to determine the result in runtime */ const leftValueRef = this.wasmExprGen(leftValue); /** create a default inst of rightValueInstType */ let rightWasmHeapType = this.wasmTypeGen.getWASMHeapType(rightValueInstType); if ( rightValueInstType.meta.name.includes( BuiltinNames.OBJECTCONSTRUCTOR, ) ) { rightWasmHeapType = emptyStructType.heapTypeRef; } if ( rightValueInstType.meta.name.includes( BuiltinNames.FUNCTIONCONSTRCTOR, ) ) { rightWasmHeapType = builtinClosureType.heapTypeRef; } const defaultRightValue = binaryenCAPI._BinaryenStructNew( this.module.ptr, arrayToPtr([]).ptr, 0, rightWasmHeapType, ); const res = this.module.call( dyntype.dyntype_instanceof, [ FunctionalFuncs.getDynContextRef(this.module), FunctionalFuncs.boxToAny(this.module, leftValueRef, leftValue), defaultRightValue, ], binaryen.i32, ); return res; } private setThisRefToClosure( closureRef: binaryen.ExpressionRef, thisRef: binaryen.ExpressionRef, ) { return binaryenCAPI._BinaryenStructSet( this.module.ptr, 1, closureRef, thisRef, ); } private getFuncRefFromClosure( closureRef: binaryen.ExpressionRef, closureTypeRef: binaryen.Type, ) { const funcRef = binaryenCAPI._BinaryenStructGet( this.module.ptr, 2, closureRef, closureTypeRef, false, ); return funcRef; } private callClosureInternal( closureRef: binaryen.ExpressionRef, funcType: FunctionType, args?: SemanticsValue[], thisRef?: binaryen.ExpressionRef, ) { const closureVarTypeRef = binaryen.getExpressionType(closureRef); const closureTmpVar = this.wasmCompiler.currentFuncCtx!.insertTmpVar(closureVarTypeRef); const setClosureTmpVarRef = this.module.local.set( closureTmpVar.index, closureRef, ); const getClosureTmpVarRef = this.module.local.get( closureTmpVar.index, closureVarTypeRef, ); const _context = binaryenCAPI._BinaryenStructGet( this.module.ptr, 0, getClosureTmpVarRef, closureVarTypeRef, false, ); const _this = thisRef ? thisRef : binaryenCAPI._BinaryenStructGet( this.module.ptr, 1, getClosureTmpVarRef, closureVarTypeRef, false, ); const funcRef = this.getFuncRefFromClosure( getClosureTmpVarRef, closureVarTypeRef, ); this.wasmCompiler.currentFuncCtx!.insert(setClosureTmpVarRef); return this.callFuncRef(funcRef, funcType, args, _this, _context); } private callBuiltinOrStaticMethod( member: MemberDescription, target: string, args?: SemanticsValue[], isBuiltin = false, ) { let funcDecl = undefined; let methodName = `${target}|${member.name}`; if (member.isStaic) { methodName = `${target}|` + '@' + `${member.name}`; funcDecl = (member.methodOrAccessor!.method!) .ref as FunctionDeclareNode; } if (isBuiltin) { methodName = UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, methodName, ); } const methodType = member.valueType as FunctionType; const returnTypeRef = this.wasmTypeGen.getWASMValueType( methodType.returnType, ); return this.callFunc( methodType, methodName, returnTypeRef, args, funcDecl, ); } private wasmOffsetCall(value: OffsetCallValue) { /* Array.xx, console.log */ const ownerType = value.owner.type as ObjectType; const meta = ownerType.meta; let isBuiltIn: boolean; const memberIdx = value.index; const member = meta.members[memberIdx]; let target = meta.name; if (BuiltinNames.builtInObjectTypes.includes(target)) { isBuiltIn = true; } else { isBuiltIn = false; if (member.isStaic) { /* Class static method */ if (member.isOwn) { target = (value.owner as VarValue).index as string; } else { let baseMeta = meta.base; while (baseMeta) { const member = baseMeta.members[memberIdx]; if (member.isOwn) { target = baseMeta.name.slice(1); break; } baseMeta = baseMeta.base; } if (!baseMeta) { throw new Error( `Can not find static field ${member.name} in inherit chain of ${meta.name}}`, ); } } } else { throw Error( `offsetCall for ${target}|${member.name} is invalid`, ); } } return this.callBuiltinOrStaticMethod( member, target, value.parameters, isBuiltIn, ); } private wasmDirectCall(value: DirectCallValue) { const owner = value.owner as VarValue; const meta = owner.shape!.meta; const method = (value.method as VarValue).ref as FunctionDeclareNode; const returnTypeRef = this.wasmTypeGen.getWASMValueType(value.type); const member = meta.findMember( UtilFuncs.getLastElemOfBuiltinName(method.name), )!; const methodIdx = this.getTruthIdx(meta, member); let thisArg = this.wasmExprGen(owner); let ownerTypeRef = this.wasmTypeGen.getWASMValueType(owner.type); if ((owner.type as ObjectType).meta.isInterface) { /* workaround: need to get the actual typeRef based on owner.shape */ ownerTypeRef = this.wasmTypeGen.objTypeMap.get(meta.name)!; thisArg = binaryenCAPI._BinaryenRefCast( this.module.ptr, thisArg, ownerTypeRef, ); } if (owner.kind === SemanticsValueKind.SUPER) { return this.callFunc( method.funcType as FunctionType, method.name, returnTypeRef, value.parameters, method, undefined, thisArg, ); } else { const methodRef = this.getObjMethod( thisArg, methodIdx, ownerTypeRef, ); return this.callFuncRef( methodRef, method.funcType as FunctionType, value.parameters, thisArg, undefined, method, ); } } private wasmFunctionCall(value: FunctionCallValue): binaryen.ExpressionRef { if (value.func instanceof FunctionCallValue) { /* Callee is returned from another function (closure) */ const closureRef = this.wasmExprGen(value.func); const funcType = value.funcType as FunctionType; return this.callClosureInternal( closureRef, funcType, value.parameters, ); } const funcType = value.funcType as FunctionType; const returnTypeRef = this.wasmTypeGen.getWASMValueType( funcType.returnType, ); const funcValue = value.func; const args = value.parameters; if (funcValue instanceof VarValue) { /* In function call, ref only can be FunctionDeclareNode */ const funcNode = funcValue.ref as FunctionDeclareNode; return this.callFunc( funcType, funcNode.name, returnTypeRef, args, funcNode, ); } else { const closureRef = this.wasmExprGen(funcValue); return this.callClosureInternal(closureRef, funcType, args); } } private wasmEnumerateKeysGet(value: EnumerateKeysGetValue) { const targetObj = value.obj; const targetObjRef = this.wasmExprGen(targetObj); switch (targetObj.type.kind) { case ValueTypeKind.OBJECT: case ValueTypeKind.INTERFACE: { const wasmFuncName = getUtilsFuncName( BuiltinNames.getPropNamesByMeta, ); const returnTypeRef = this.wasmTypeGen.getWASMValueType( value.type, ); return this.module.call( wasmFuncName, [targetObjRef], returnTypeRef, ); } case ValueTypeKind.ANY: { return FunctionalFuncs.getObjKeys(this.module, targetObjRef); } default: { throw new UnimplementError( `${targetObj.type.kind} kind is not supported yet`, ); } } } private wasmClosureCall(value: ClosureCallValue): binaryen.ExpressionRef { const funcType = value.funcType as FunctionType; const closureRef = this.wasmExprGen(value.func as VarValue); return this.callClosureInternal(closureRef, funcType, value.parameters); } private callClassMethod( methodType: FunctionType, realReturnType: ValueType, calledName: string, thisRef: binaryen.ExpressionRef, valueType: ValueType, args?: SemanticsValue[], ): binaryen.ExpressionRef { if (BuiltinNames.genericBuiltinMethods.includes(calledName)) { if (valueType instanceof ArrayType) { const methodSuffix = this.wasmTypeGen.getObjSpecialSuffix(valueType); calledName = calledName.concat(methodSuffix); } else { throw new Error( 'Generic builtin method only support array type', ); } } const returnTypeRef = this.wasmTypeGen.getWASMValueType( methodType.returnType, ); let res = this.callFunc( methodType, calledName, returnTypeRef, args, undefined, undefined, thisRef, ); /* methodCallResultRef's type may not match the real return type * if real return type is not primitive type, we should do cast. */ if (this.wasmTypeGen.hasHeapType(realReturnType)) { /* workaround: now binaryen_116version has bug: * it will generate ref.cast nullref when use callExpression to cast directly */ const callResTypeRef = binaryen.anyref; const calledResVar = this.wasmCompiler.currentFuncCtx!.insertTmpVar(callResTypeRef); this.wasmCompiler.currentFuncCtx!.insert( this.module.local.set(calledResVar.index, res), ); res = binaryenCAPI._BinaryenRefCast( this.module.ptr, this.module.local.get(calledResVar.index, callResTypeRef), this.wasmTypeGen.getWASMValueType(realReturnType), ); } return res; } private callClassStaticMethod( ownValue: ObjectType, methodName: string, args?: SemanticsValue[], ) { const foundMember = this.getMemberByName(ownValue.meta, methodName); const methodMangledName = this.wasmCompiler.getMethodMangledName( foundMember, ownValue.meta, ); // workaround: reason /* Currently, value.funcType is different with member type */ const funcType = foundMember.valueType as FunctionType; /* get return type */ const returnTypeRef = this.wasmTypeGen.getWASMValueType( funcType.returnType, ); return this.callFunc(funcType, methodMangledName, returnTypeRef, args); } private wasmAnyCall(value: AnyCallValue) { /* call dynamic js function, which will callback to wasm finally */ const anyFuncRef = this.wasmExprGen(value.anyFunc); const dynamicArg = this.generateDynamicArg(value.parameters); return this.module.call( dyntype.dyntype_invoke, [ FunctionalFuncs.getDynContextRef(this.module), this.module.i32.const(0), anyFuncRef, dynamicArg, ], binaryen.anyref, ); } private wasmVtableCall(value: VTableCallValue) { const owner = value.owner; const meta = owner.shape!.meta; const member = meta.members[value.index]; const methodIdx = this.getTruthIdx(meta, member); const ownerRef = this.wasmExprGen(owner); const ownerTypeRef = this.wasmTypeGen.getWASMValueType(owner.type); switch (owner.type.kind) { case ValueTypeKind.OBJECT: { if (BuiltinNames.builtInObjectTypes.includes(meta.name)) { const methodName = UtilFuncs.getBuiltinClassMethodName( meta.name, member.name, ); const args: binaryen.ExpressionRef[] = []; if (value.parameters) { value.parameters.forEach((param) => { const paramRef = this.wasmExprGen(param); args.push(paramRef); }); } return this.callClassMethod( value.funcType, value.funcType.returnType, methodName, ownerRef, owner.type, value.parameters, ); } else { const methodRef = this.getObjMethod( ownerRef, methodIdx, ownerTypeRef, ); return this.callFuncRef( methodRef, value.funcType, value.parameters, ownerRef, ); } } case ValueTypeKind.STRING: { if (getConfig().enableStringRef) { /* fallback to libdyntype */ const nonFallbackMethods = [ 'indexOf', 'split', 'match', 'search', 'charCodeAt', ]; if (!nonFallbackMethods.includes(member.name)) { let invokeArgs = [ new CastValue( SemanticsValueKind.VALUE_CAST_ANY, owner.type, owner, ) as SemanticsValue, ]; if (value.parameters) { invokeArgs = invokeArgs.concat(value.parameters); } return this.module.call( dyntype.dyntype_to_string, [ FunctionalFuncs.getDynContextRef(this.module), this.dyntypeInvoke(member.name, invokeArgs), ], binaryenCAPI._BinaryenTypeStringref(), ); } } /* fallthrough */ } default: { /* workaround: arr.push is vtableCall */ const calledName = `${BuiltinNames.builtinModuleName}|${meta.name}|${member.name}`; /* workaround: method.valueType.returnType various from value.funcType.returnType */ const realReturnType = value.funcType.returnType; return this.callClassMethod( member.valueType as FunctionType, realReturnType, calledName, ownerRef, owner.type, value.parameters, ); } } } private wasmDynamicCall(value: DynamicCallValue): binaryen.ExpressionRef { const methodName = value.name; const owner = value.owner; switch (owner.type.kind) { case ValueTypeKind.UNION: case ValueTypeKind.ANY: { /* Fallback to libdyntype */ let invokeArgs = [owner]; if (value.parameters) { invokeArgs = invokeArgs.concat(value.parameters); } return this.dyntypeInvoke(methodName, invokeArgs); } case ValueTypeKind.ARRAY: case ValueTypeKind.FUNCTION: case ValueTypeKind.BOOLEAN: case ValueTypeKind.NUMBER: case ValueTypeKind.STRING: { if ( getConfig().enableStringRef && owner.type.kind === ValueTypeKind.STRING ) { let invokeArgs = [owner]; if (value.parameters) { invokeArgs = invokeArgs.concat(value.parameters); } return this.dyntypeInvoke(methodName, invokeArgs); } else { const className = 'String'; const builtInMeta = owner.shape!.meta!; const foundMember = this.getMemberByName( builtInMeta, methodName, ); const methodType = foundMember.valueType as FunctionType; const thisRef = this.wasmExprGen(owner); const calledName = `${BuiltinNames.builtinModuleName}|${className}|${methodName}`; return this.callClassMethod( methodType, methodType.returnType, calledName, thisRef, owner.type, value.parameters, ); } } case ValueTypeKind.WASM_ARRAY: { throw new UnimplementError( `unimplement wasmDynamicCall when type is WASM_ARRAY`, ); } default: throw new UnimplementError( `unimplement wasmDynamicCall in : ${value}`, ); } } private wasmShapeCall(value: ShapeCallValue): binaryen.ExpressionRef { /* When specialized (such as Array): * the original unspecialized type is stored in shape, and the specific specialized type is stored in type */ const owner = value.owner as VarValue; const shapeMeta = owner.shape!.meta!; const shapeMember = shapeMeta.members[value.index]; const args = value.parameters; let target = shapeMeta.name; /* Workaround: should use meta.isBuiltin, but currently only class defined inside src/semantics/builtin.ts will be marked as builtin. After that issue fixed, we should modify the code here */ if ( BuiltinNames.builtInObjectTypes.some((elem) => { if (target.includes(elem)) { target = elem; return true; } else { return false; } }) ) { return this.callBuiltinOrStaticMethod( shapeMember, target, value.parameters, true, ); } switch (owner.type.kind) { case ValueTypeKind.OBJECT: { if (owner.ref instanceof ObjectType) { return this.callClassStaticMethod( owner.ref, shapeMember.name, value.parameters, ); } else { const ownerType = owner.type as ObjectType; const typeMeta = ownerType.meta; const typeMember = typeMeta.findMember( shapeMember.name, ) as MemberDescription; const thisRef = this.wasmExprGen(owner); return this.getInstProperty( thisRef, ownerType, typeMeta, typeMember, true, args, ); } } case ValueTypeKind.ARRAY: { // workaround: /* Array type can be specialized, so we should get the type meta */ const typeMeta = (owner.type as ArrayType).meta; const member = typeMeta.members[value.index]; const thisRef = this.wasmExprGen(owner); /* array builtin method call */ let methodName = member.name; for (const builtinMethod of BuiltinNames.genericBuiltinMethods) { if (builtinMethod.includes(member.name)) { methodName = builtinMethod; break; } } const methodSuffix = this.wasmTypeGen.getObjSpecialSuffix( owner.type as ArrayType, ); methodName = methodName.concat(methodSuffix); const memberFuncType = member.valueType as FunctionType; const returnTypeRef = this.wasmTypeGen.getWASMValueType( memberFuncType.returnType, ); const methodCallResultRef = this.callFunc( memberFuncType, methodName, returnTypeRef, args, undefined, undefined, thisRef, ); /* methodCallResultRef's type may not match the real return type * if real return type is not primitive type, we should do cast. */ let res = methodCallResultRef; if (this.wasmTypeGen.hasHeapType(memberFuncType.returnType)) { res = binaryenCAPI._BinaryenRefCast( this.module.ptr, methodCallResultRef, returnTypeRef, ); } return res; } default: { throw Error(`TODO: ${value.type.kind}`); } } } private wasmAnyCast(value: CastValue): binaryen.ExpressionRef { const fromValue = value.value; const fromType = fromValue.type; const toType = value.type; switch (value.kind) { case SemanticsValueKind.ANY_CAST_VALUE: case SemanticsValueKind.UNION_CAST_VALUE: { const fromValueRef = this.wasmExprGen(fromValue); return FunctionalFuncs.unboxAnyToBase( this.module, fromValueRef, toType.kind, ); } case SemanticsValueKind.VALUE_CAST_ANY: case SemanticsValueKind.UNION_CAST_ANY: case SemanticsValueKind.VALUE_CAST_UNION: { const fromValueRef = this.wasmExprGen(fromValue); return FunctionalFuncs.boxToAny( this.module, fromValueRef, fromValue, ); } case SemanticsValueKind.OBJECT_CAST_ANY: { return this.wasmObjTypeCastToAny(value); } case SemanticsValueKind.ANY_CAST_OBJECT: case SemanticsValueKind.UNION_CAST_OBJECT: { const fromValueRef = this.wasmExprGen(fromValue); const toTypeRef = this.wasmTypeGen.getWASMValueType(toType); return FunctionalFuncs.unboxAnyToExtref( this.module, fromValueRef, toTypeRef, ); } case SemanticsValueKind.OBJECT_CAST_VALUE: { if (toType.kind === ValueTypeKind.NULL) { /* Sometimes the function may be inferred to return a null, e.g.: function foo() { const a: A | null = null; return a; } */ return this.module.ref.null( this.wasmTypeGen.getWASMType(fromType), ); } else { throw new UnimplementError( `OBJECT_CAST_VALUE from ${fromType} to ${toType}`, ); } } case SemanticsValueKind.OBJECT_CAST_UNION: { if (fromValue instanceof NewLiteralArrayValue) { // box the literal array to any return this.wasmObjTypeCastToAny(value); } else { const fromValueRef = this.wasmExprGen(fromValue); return FunctionalFuncs.boxToAny( this.module, fromValueRef, fromValue, ); } } default: throw new UnimplementError(`wasmCastValue: ${value}`); } } private wasmValueCast(value: CastValue) { const fromType = value.value.type; const fromValueRef = this.wasmExprGen(value.value); const fromTypeRef = this.wasmTypeGen.getWASMType(fromType); const toType = value.type; /* if toType is BOOLEAN, then need to generate condition */ if (toType.kind === ValueTypeKind.BOOLEAN) { return FunctionalFuncs.generateCondition( this.module, fromValueRef, fromType.kind, ); } /* do type cast */ if (fromType.kind === ValueTypeKind.INT) { if (toType.kind === ValueTypeKind.NUMBER) { return FunctionalFuncs.convertTypeToF64( this.module, fromValueRef, fromTypeRef, value.isSigned, ); } else if (toType.kind === ValueTypeKind.WASM_I64) { return FunctionalFuncs.convertTypeToI64( this.module, fromValueRef, fromTypeRef, value.isSigned, ); } else if (toType.kind === ValueTypeKind.WASM_F32) { return FunctionalFuncs.convertTypeToF32( this.module, fromValueRef, fromTypeRef, value.isSigned, ); } } else if (fromType.kind === ValueTypeKind.NUMBER) { if (toType.kind === ValueTypeKind.INT) { return FunctionalFuncs.convertTypeToI32( this.module, fromValueRef, fromTypeRef, value.isSigned, ); } else if (toType.kind === ValueTypeKind.WASM_I64) { return FunctionalFuncs.convertTypeToI64( this.module, fromValueRef, fromTypeRef, value.isSigned, ); } else if (toType.kind === ValueTypeKind.WASM_F32) { return FunctionalFuncs.convertTypeToF32( this.module, fromValueRef, fromTypeRef, value.isSigned, ); } } else if (fromType.kind === ValueTypeKind.WASM_I64) { if (toType.kind === ValueTypeKind.INT) { return FunctionalFuncs.convertTypeToI32( this.module, fromValueRef, fromTypeRef, ); } else if (toType.kind === ValueTypeKind.NUMBER) { return FunctionalFuncs.convertTypeToF64( this.module, fromValueRef, fromTypeRef, value.isSigned, ); } else if (toType.kind === ValueTypeKind.WASM_F32) { return FunctionalFuncs.convertTypeToF32( this.module, fromValueRef, fromTypeRef, value.isSigned, ); } } else if (fromType.kind === ValueTypeKind.WASM_F32) { if (toType.kind === ValueTypeKind.INT) { return FunctionalFuncs.convertTypeToI32( this.module, fromValueRef, fromTypeRef, value.isSigned, ); } else if (toType.kind === ValueTypeKind.NUMBER) { return FunctionalFuncs.convertTypeToF64( this.module, fromValueRef, fromTypeRef, value.isSigned, ); } else if (toType.kind === ValueTypeKind.WASM_I64) { return FunctionalFuncs.convertTypeToI64( this.module, fromValueRef, fromTypeRef, value.isSigned, ); } } else if (fromType.kind === ValueTypeKind.BOOLEAN) { if (toType.kind === ValueTypeKind.NUMBER) { return FunctionalFuncs.convertTypeToF64( this.module, fromValueRef, fromTypeRef, ); } } else if (fromType.kind === ValueTypeKind.ENUM) { if (toType.kind === ValueTypeKind.NUMBER) { return FunctionalFuncs.convertTypeToF64( this.module, fromValueRef, fromTypeRef, ); } } throw new UnimplementError(`wasmValueCast: ${value}`); } private parseArguments( funcType: FunctionType, envArgs: binaryen.ExpressionRef[], args?: SemanticsValue[], funcNode?: FunctionDeclareNode, ) { const envArgLen = envArgs.length; const paramTypes = funcType.argumentsType; const callerArgs: binaryen.ExpressionRef[] = new Array( paramTypes.length + envArgLen, ); /* parse @context and @this */ for (let i = 0; i < envArgLen; i++) { callerArgs[i] = envArgs[i]; } /* parse optional param as undefined */ for (let i = 0; i < paramTypes.length; i++) { /* workaround: when promise generic type is void, function call arguments may be empty. We should add undefined as argument here. new Promise((resolve, reject) => { resolve(); }); */ if ( funcType.isOptionalParams[i] || funcType.argumentsType[i].kind === ValueTypeKind.TYPE_PARAMETER ) { callerArgs[i + envArgLen] = FunctionalFuncs.generateDynUndefined(this.module); } } /* parse default params */ if (funcNode && funcNode.parameters) { for (let i = 0; i < funcNode.parameters.length; i++) { const defaultParam = funcNode.parameters[i]; if (defaultParam.initValue) { const initValue = defaultParam.initValue; let defaultArg = this.wasmExprGen(defaultParam.initValue); if ( defaultParam.type.kind === ValueTypeKind.ANY && initValue.type.kind !== ValueTypeKind.ANY ) { /* Workaround: for default parameters (e.g. b: any = 8), the type of the initValue is treated as a number and not casted to any, which will make the generated wasm module contained mismatched types */ defaultArg = FunctionalFuncs.boxToAny( this.module, defaultArg, initValue, ); } callerArgs[i + envArgLen] = defaultArg; } } } if (!args) { if (funcType.restParamIdx !== -1) { const restType = paramTypes[funcType.restParamIdx] as ArrayType; callerArgs[funcType.restParamIdx + envArgLen] = this.initArray( restType, [], ); } return callerArgs; } /* parse regular args, real args don't contain @context and @this */ for (let i = 0; i < args.length; i++) { if (funcType.restParamIdx === i) { break; } callerArgs[i + envArgLen] = this.wasmExprGen(args[i]); } /* parse rest params */ if (funcType.restParamIdx !== -1) { const restType = paramTypes[funcType.restParamIdx]; if (restType instanceof ArrayType) { if (args.length > funcType.restParamIdx) { callerArgs[funcType.restParamIdx + envArgLen] = this.initArray( restType, args.slice(funcType.restParamIdx), ); } else { callerArgs[funcType.restParamIdx + envArgLen] = this.initArray(restType, []); } } else { Logger.error(`rest type is not array`); } } return callerArgs; } private initArray(arrType: ArrayType, elements: SemanticsValue[]) { return this.wasmElemsToArr(elements, arrType); } /* Currently we don't believe the index provided by semantic tree, semantic tree treat all method/accessors as instance field, but in binaryen backend we put all these into vtable, so every getter/setter pair will occupies two vtable slots */ private fixVtableIndex( meta: ObjectDescription, member: MemberDescription, isSetter = false, ) { const members = meta.members; const bound = members.findIndex((m) => m.name === member.name); let index = bound; if (index < 0) { throw new Error( `get field index failed, field name is ${member.name}`, ); } for (let i = 0; i < bound; i++) { if (members[i].type === MemberType.FIELD) { index--; } /** it occupies two slots */ if (members[i].hasGetter || members[i].hasSetter) { index++; } } if (isSetter) { index++; } return index; } private fixFieldIndex( meta: ObjectDescription, member: MemberDescription, isStatic = false, ) { const members = meta.members; const bound = members.findIndex((m) => m.name === member.name); let index = 0; for (let i = 0; i < bound; i++) { if (members[i].type === MemberType.FIELD) { if (isStatic) { if (members[i].isStaic) { index++; } } else { if (!members[i].isStaic) { index++; } } } } return index; } private setObjField( objRef: binaryen.ExpressionRef, fieldIdx: number, targetValueRef: binaryen.ExpressionRef, ) { return binaryenCAPI._BinaryenStructSet( this.module.ptr, fieldIdx + 1, objRef, targetValueRef, ); } private setObjMethod( objRef: binaryen.ExpressionRef, methodIdx: number, objTypeRef: binaryen.Type, targetValueRef: binaryen.ExpressionRef, ) { const vtableRef = binaryenCAPI._BinaryenStructGet( this.module.ptr, 0, objRef, objTypeRef, false, ); return binaryenCAPI._BinaryenStructSet( this.module.ptr, /** because the first index is point to meta, so methodIdx should plus 1 */ methodIdx + 1, vtableRef, targetValueRef, ); } private getBuiltinObjField( objRef: binaryen.ExpressionRef, fieldIdx: number, objTypeRef: binaryen.Type, ) { return binaryenCAPI._BinaryenStructGet( this.module.ptr, fieldIdx, objRef, objTypeRef, false, ); } private getObjField( objRef: binaryen.ExpressionRef, fieldIdx: number, objTypeRef: binaryen.Type, ) { return binaryenCAPI._BinaryenStructGet( this.module.ptr, fieldIdx + 1, objRef, objTypeRef, false, ); } private getObjMethod( objRef: binaryen.ExpressionRef, methodIdx: number, objTypeRef: binaryen.Type, ) { const vtableRef = binaryenCAPI._BinaryenStructGet( this.module.ptr, 0, objRef, objTypeRef, false, ); return binaryenCAPI._BinaryenStructGet( this.module.ptr, /** because the first index is point to meta, so methodIdx should plus 1 */ methodIdx + 1, vtableRef, binaryen.getExpressionType(vtableRef), false, ); } private getObjMethodAsClosure( objRef: binaryen.ExpressionRef, methodIdx: number, objTypeRef: binaryen.Type, methodType: FunctionType, ) { const methodRef = this.getObjMethod(objRef, methodIdx, objTypeRef); return this.getClosureOfFunc(methodRef, methodType, objRef); } private callFuncRef( targetFunction: binaryen.ExpressionRef, funcType: FunctionType, args?: SemanticsValue[], _this?: binaryen.ExpressionRef, _context?: binaryen.ExpressionRef, funcDecl?: FunctionDeclareNode, ) { const returnTypeRef = this.wasmTypeGen.getWASMValueType( funcType.returnType, ); if (!_context) { _context = this.wasmCompiler.emptyRef; } if (!_this) { _this = this.wasmCompiler.emptyRef; } const envArgs: binaryen.ExpressionRef[] = [_context, _this]; const callArgsRefs = this.parseArguments( funcType, envArgs, args, funcDecl, ); return binaryenCAPI._BinaryenCallRef( this.module.ptr, targetFunction, arrayToPtr(callArgsRefs).ptr, callArgsRefs.length, returnTypeRef, false, ); } private callFunc( funcType: FunctionType, funcName: string, returnType: binaryen.Type, args?: SemanticsValue[], funcDecl?: FunctionDeclareNode, context?: binaryen.ExpressionRef, thisArg?: binaryen.ExpressionRef, ) { if (!context) { context = this.wasmCompiler.emptyRef; } if (!thisArg) { thisArg = this.wasmCompiler.emptyRef; } const envArgs: binaryen.ExpressionRef[] = [context, thisArg]; const callArgsRefs = this.parseArguments( funcType, envArgs, args, funcDecl, ); return this.module.call(funcName, callArgsRefs, returnType); } private wasmObjFieldSet( value: ShapeSetValue | OffsetSetValue | VTableSetValue, rightValue?: SemanticsValue, ) { const owner = value.owner as VarValue; const shapeMeta = owner.shape!.meta; const shapeMember = shapeMeta.members[value.index]; const ownerType = owner.type as ObjectType; let targetValue = value.value!; if (rightValue) { targetValue = rightValue; } const typeMeta = ownerType.meta; const typeMember = typeMeta.findMember( shapeMember.name, ) as MemberDescription; return this.setInstProperty( this.wasmExprGen(owner), ownerType, typeMeta, typeMember, targetValue, ); } private setInstProperty( thisRef: binaryen.ExpressionRef, ownerType: ObjectType, typeMeta: ObjectDescription, typeMember: MemberDescription, targetValue: SemanticsValue, ) { const isSetter = typeMember.hasSetter ? true : false; if (isSetter) { return this.getInstProperty( thisRef, ownerType, typeMeta, typeMember, isSetter, [targetValue], isSetter, ); } else { const propertyIdx = this.getTruthIdx( typeMeta, typeMember, typeMember.hasSetter, ); if (typeMeta.isInterface) { return this.setInfcProperty( typeMember, ownerType, thisRef, propertyIdx, targetValue, ); } else { return this.setObjProperty( typeMember, ownerType, thisRef, propertyIdx, targetValue, ); } } } private setInfcProperty( member: MemberDescription, infcType: ValueType, thisRef: binaryen.ExpressionRef, propertyIdx: number, targetValue: SemanticsValue, ) { const metaRef = FunctionalFuncs.getWASMObjectMeta(this.module, thisRef); const memberNameRef = this.getStringOffset(member.name); let flag = ItableFlag.ALL; if (member.hasSetter) { flag = ItableFlag.SETTER; } const flagAndIndexRef = this.getPropFlagAndIdxRefFromObj( metaRef, memberNameRef, flag, ); const propTypeIdRef = this.getPropTypeIdRefFromObj( metaRef, memberNameRef, flag, ); const propType = member.hasSetter ? (member.setter as VarValue).type : member.valueType; const targetValueRef = this.wasmExprGen(targetValue); /* TODO: workaround: quick path may fail, since cast failure */ const infcDescTypeRef = this.wasmTypeGen.getWASMObjOriType(infcType); const castedObjRef = binaryenCAPI._BinaryenRefCast( this.module.ptr, thisRef, infcDescTypeRef, ); const ifShapeCompatibal = FunctionalFuncs.isShapeCompatible( this.module, infcType.typeId, metaRef, ); let ifCompatibalTrue: binaryen.ExpressionRef; if (targetValue.type.kind === ValueTypeKind.FUNCTION) { /* if property's value type is function, and typeid is equal, then we can set property to vtable */ ifCompatibalTrue = this.setObjMethod( castedObjRef, propertyIdx, infcDescTypeRef, this.getFuncRefFromClosure( targetValueRef, this.wasmTypeGen.getWASMValueType(targetValue.type), ), ); } else { /* if property's value type is not function, then it must be a field */ ifCompatibalTrue = this.setObjField( castedObjRef, propertyIdx, targetValueRef, ); } const ifCompatibalFalse = this.dynSetInfcProperty( thisRef, flagAndIndexRef, targetValue.type, member.isOptional, propTypeIdRef, targetValueRef, ); /* set property from interface */ return this.module.if( ifShapeCompatibal, ifCompatibalTrue, ifCompatibalFalse, ); } private setObjProperty( member: MemberDescription, objType: ValueType, thisRef: binaryen.ExpressionRef, propertyIdx: number, targetValue: SemanticsValue, ) { const thisTypeRef = this.wasmTypeGen.getWASMType(objType); const targetValueRef = this.wasmExprGen(targetValue); let res: binaryen.ExpressionRef; if (member.type === MemberType.FIELD) { res = this.setObjField(thisRef, propertyIdx, targetValueRef); } else { res = this.setObjMethod( thisRef, propertyIdx, thisTypeRef, targetValueRef, ); } return res; } private getInstProperty( thisRef: binaryen.ExpressionRef, ownerType: ObjectType, typeMeta: ObjectDescription, typeMember: MemberDescription, isCall = false, args?: SemanticsValue[], isSetter = false, ) { const propertyIdx = this.getTruthIdx(typeMeta, typeMember, isSetter); if (typeMeta.isInterface) { return this.getInfcProperty( typeMember, ownerType, thisRef, propertyIdx, isCall, args, isSetter, ); } else { return this.getObjProperty( typeMember, ownerType, thisRef, propertyIdx, isCall, args, isSetter, ); } } private getInfcProperty( member: MemberDescription, infcType: ValueType, thisRef: binaryen.ExpressionRef, propertyIdx: number, isCall = false, args?: SemanticsValue[], isSetter = false, ) { const metaRef = FunctionalFuncs.getWASMObjectMeta(this.module, thisRef); const memberNameRef = this.getStringOffset(member.name); const flag = ItableFlag.ALL; const flagAndIndexRef = this.getPropFlagAndIdxRefFromObj( metaRef, memberNameRef, flag, ); const propTypeIdRef = this.getPropTypeIdRefFromObj( metaRef, memberNameRef, flag, ); const propType = isSetter ? (member.setter as VarValue).type : member.hasGetter ? (member.getter as VarValue).type : member.valueType; /* TODO: workaround: quick path may fail, since cast failure */ const infcDescTypeRef = this.wasmTypeGen.getWASMObjOriType(infcType); const castedObjRef = binaryenCAPI._BinaryenRefCast( this.module.ptr, thisRef, infcDescTypeRef, ); const ifShapeCompatibal = FunctionalFuncs.isShapeCompatible( this.module, infcType.typeId, metaRef, ); let ifCompatibalTrue: binaryen.ExpressionRef; if (propType.kind === ValueTypeKind.FUNCTION) { /* if property's value type is function, and typeid is equal, then we can get property from vtable */ /* methodRef get from vtable is a funcref, we need to box it to closure */ ifCompatibalTrue = this.getObjMethodAsClosure( castedObjRef, propertyIdx, infcDescTypeRef, propType as FunctionType, ); } else { /* if property's value type is not function, then it must be a field */ ifCompatibalTrue = this.getObjField( castedObjRef, propertyIdx, infcDescTypeRef, ); } const ifCompatibalFalse = this.dynGetInfcProperty( thisRef, flagAndIndexRef, propType, member.isOptional, propTypeIdRef, ); /* get property from interface */ let res = this.module.if( ifShapeCompatibal, ifCompatibalTrue, ifCompatibalFalse, ); /* If isCall or member is an accessor, call the memberRef, and get the result */ if ( member.type === MemberType.ACCESSOR || (propType.kind === ValueTypeKind.FUNCTION && isCall) ) { /* the function we get must be a closure, we need to callClosureInternal */ if (member.isOptional) { /* if member is optional, need to do unbox */ res = FunctionalFuncs.unboxAnyToExtref( this.module, res, this.wasmTypeGen.getWASMValueType(propType as FunctionType), ); } res = this.callClosureInternal( res, propType as FunctionType, args, thisRef, ); } return res; } private getObjProperty( member: MemberDescription, objType: ValueType, thisRef: binaryen.ExpressionRef, memberIdx: number, isCall = false, args?: SemanticsValue[], isSetter = false, ) { const thisTypeRef = this.wasmTypeGen.getWASMType(objType); const propType = isSetter ? (member.setter as VarValue).type : member.hasGetter ? (member.getter as VarValue).type : member.valueType; let res: binaryen.ExpressionRef; if (member.type === MemberType.FIELD) { const fieldValueRef = this.getObjField( thisRef, memberIdx, thisTypeRef, ); if (propType.kind === ValueTypeKind.FUNCTION) { /* If propType is FunctionType, then we must reset @this value in closure */ const fieldTmpVar = this.wasmCompiler.currentFuncCtx!.insertTmpVar( this.wasmTypeGen.getWASMValueType(propType), ); const setValueRef = this.module.local.set( fieldTmpVar.index, fieldValueRef, ); this.wasmCompiler.currentFuncCtx!.insert(setValueRef); const getValueRef = this.module.local.get( fieldTmpVar.index, fieldTmpVar.type, ); this.wasmCompiler.currentFuncCtx!.insert( this.setThisRefToClosure(getValueRef, thisRef), ); res = getValueRef; if (isCall) { /* If is called, then call the field closure */ res = this.callClosureInternal( res, propType as FunctionType, args, ); } } else { res = fieldValueRef; } } else { if ( member.type === MemberType.ACCESSOR || (member.type === MemberType.METHOD && isCall) ) { res = this.getObjMethod(thisRef, memberIdx, thisTypeRef); res = this.callFuncRef( res, propType as FunctionType, args, thisRef, ); } else { res = this.getObjMethodAsClosure( thisRef, memberIdx, thisTypeRef, propType as FunctionType, ); } } return res; } private getPropFlagAndIdxRefFromObj( meta: binaryen.ExpressionRef, name: binaryen.ExpressionRef, flag: ItableFlag, ) { return this.module.call( BuiltinNames.findPropertyFlagAndIndex, [meta, name, this.module.i32.const(flag)], binaryen.i32, ); } private getPropTypeIdRefFromObj( meta: binaryen.ExpressionRef, name: binaryen.ExpressionRef, flag: ItableFlag, ) { return this.module.call( BuiltinNames.findPropertyType, [meta, name, this.module.i32.const(flag)], binaryen.i32, ); } /** return method in the form of closure */ private getClosureOfFunc( func: binaryen.ExpressionRef, type: FunctionType, _this: binaryen.ExpressionRef, ) { const closureType = this.wasmTypeGen.getWASMValueHeapType(type); const res = binaryenCAPI._BinaryenStructNew( this.module.ptr, arrayToPtr([this.wasmCompiler.emptyRef, _this, func]).ptr, 3, closureType, ); return res; } private wasmNewLiteralObj(value: NewLiteralObjectValue) { const objHeapTypeRef = this.wasmTypeGen.getWASMHeapType(value.type); const vtableHeapTypeRef = this.wasmTypeGen.getWASMVtableHeapType( value.type, ); const members = (value.type as ObjectType).meta.members; const propRefList: binaryen.ExpressionRef[] = []; const vtable: binaryen.ExpressionRef[] = []; for (let i = 0; i < members.length; i++) { /* eg. arr = [{a:1}, {a:2}, {a:3, b:4}] TSC treate arr type is Array<{a:number, b?: number} | {a:number, b:number}> */ if (!value.initValues[i]) { propRefList.push( FunctionalFuncs.generateDynUndefined(this.module), ); } else { const memberValueRef = this.wasmExprGen(value.initValues[i]); if (members[i].type === MemberType.FIELD) { propRefList.push(memberValueRef); } else if (members[i].type === MemberType.METHOD) { vtable.push(memberValueRef); } } } const vtableRef = this.wasmTypeGen.getWASMVtableInst(value.type); propRefList.unshift(vtableRef); const objectLiteralValueRef = binaryenCAPI._BinaryenStructNew( this.module.ptr, arrayToPtr(propRefList).ptr, propRefList.length, objHeapTypeRef, ); return objectLiteralValueRef; } private wasmObjCast(value: CastValue) { const oriValueRef = this.wasmExprGen(value.value); const oriValueType = value.value.type as ObjectType; const toValueType = value.type as ObjectType; if (toValueType.flags === ObjectTypeFlag.UNION) { return this.wasmObjTypeCastToAny(value); } if (oriValueType instanceof UnionType) { const toTypeRef = this.wasmTypeGen.getWASMValueType(toValueType); return FunctionalFuncs.unboxAnyToExtref( this.module, oriValueRef, toTypeRef, ); } switch (oriValueType.meta.type) { case ObjectDescriptionType.OBJECT_INSTANCE: case ObjectDescriptionType.OBJECT_CLASS: case ObjectDescriptionType.OBJECT_LITERAL: { if (toValueType.meta.isInterface) { return oriValueRef; } /** check if it is upcasting */ let fromType: ObjectType | undefined = oriValueType; while (fromType) { if (fromType.equals(toValueType)) { return oriValueRef; } fromType = fromType.super; } const toValueWasmType = this.wasmTypeGen.getWASMType(toValueType); return binaryenCAPI._BinaryenRefCast( this.module.ptr, oriValueRef, toValueWasmType, ); } case ObjectDescriptionType.INTERFACE: { if (toValueType.meta.isInterface) { /* interfaceObj to interfaceObj */ return oriValueRef; } else { /** need to check the cast can be successful */ return this.infcCastToObj(oriValueRef, toValueType); } } } } private infcCastToObj(ref: binaryen.ExpressionRef, toType: ObjectType) { const meta = FunctionalFuncs.getWASMObjectMeta(this.module, ref); const typeIdRef = FunctionalFuncs.getFieldFromMetaByOffset( this.module, meta, MetaDataOffset.TYPE_ID_OFFSET, ); const canbeCasted = this.module.i32.eq( typeIdRef, this.module.i32.const(toType.typeId), ); return this.module.if( canbeCasted, binaryenCAPI._BinaryenRefCast( this.module.ptr, ref, this.wasmTypeGen.getWASMType(toType), ), this.module.unreachable(), ); } private wasmNewClass(value: NewConstructorObjectValue) { const objectTypeRef = this.wasmTypeGen.getWASMType(value.type); /* currently, ctor is only in a seperate field, not be put into members */ const metaInfo = (value.type as ObjectType).meta; if (!metaInfo.ctor) { const className = metaInfo.name; if (BuiltinNames.fallbackConstructors.includes(metaInfo.name)) { /* workaround: Error constructor is not defined, so we can fallback temporarily */ /* Fallback to libdyntype */ return this.dyntypeInvoke(className, value.parameters, true); } else { const ctorName = UtilFuncs.getBuiltinClassCtorName(className); const ctorInfcDefination = GetBuiltinObjectType( className.concat(BuiltinNames.ctorName), ); const ctorMember = ctorInfcDefination.meta.findConstructor(); if (!ctorMember) { throw Error(`can not find ctor type in ${className}`); } return this.callFunc( ctorMember.valueType as FunctionType, ctorName, objectTypeRef, value.parameters, ); } } else { const ctorFuncDecl = ( metaInfo.ctor!.methodOrAccessor!.method! as VarValue ).ref as FunctionDeclareNode; const thisArg = this.wasmTypeGen.getWASMThisInst(value.type); return this.callFunc( metaInfo.ctor!.valueType as FunctionType, ctorFuncDecl.name, objectTypeRef, value.parameters, ctorFuncDecl, undefined, thisArg, ); } } private getClassStaticField( member: MemberDescription, meta: ObjectDescription, objType: ObjectType, ) { /* class A; A.yy */ if (member.type === MemberType.FIELD && member.isStaic) { const valueIdx = this.fixFieldIndex(meta, member, true); const staticFieldsTypeRef = this.wasmTypeGen.getWASMStaticFieldsType(objType); const name = meta.name + '|static_fields'; const staticFields = binaryenCAPI._BinaryenGlobalGet( this.module.ptr, UtilFuncs.getCString(name), staticFieldsTypeRef, ); return binaryenCAPI._BinaryenStructGet( this.module.ptr, valueIdx, staticFields, staticFieldsTypeRef, false, ); } else { throw Error(`${member} is not a static field`); } } private wasmObjFieldGet( value: DirectGetValue | ShapeGetValue | OffsetGetValue | VTableGetValue, ) { /* Workaround: ShapeGetValue's field index now based on its origin shape, not objectType */ const owner = value.owner; const shapeMeta = owner.shape!.meta; const shapeMember = shapeMeta.members[value.index]; switch (owner.type.kind) { case ValueTypeKind.UNION: case ValueTypeKind.ANY: { /* let o: A|null = new A; o'field type is real type, not any type */ const objRef = this.wasmExprGen(owner); const propNameRef = this.getStringOffset(shapeMember.name); const memberType = shapeMember.valueType; const anyObjProp = FunctionalFuncs.getDynObjProp( this.module, objRef, propNameRef, ); return FunctionalFuncs.unboxAny( this.module, anyObjProp, memberType.kind, this.wasmTypeGen.getWASMType(memberType), ); } case ValueTypeKind.OBJECT: { const ownerType = owner.type as ObjectType; const typeMeta = ownerType.meta; const typeMember = typeMeta.findMember( shapeMember.name, ) as MemberDescription; if ( owner instanceof VarValue && owner.ref instanceof ObjectType ) { /* static field get */ return this.getClassStaticField( typeMember, typeMeta, ownerType, ); } else { const objRef = this.wasmExprGen(owner); if ( BuiltinNames.builtInObjectTypes.includes(typeMeta.name) ) { const propertyIdx = this.getTruthIdx( typeMeta, typeMember, ); return this.getBuiltinObjField( objRef, propertyIdx, this.wasmTypeGen.getWASMType(ownerType), ); } else { return this.getInstProperty( objRef, ownerType, typeMeta, typeMember, ); } } } case ValueTypeKind.ARRAY: { const objRef = this.wasmExprGen(owner); if (shapeMember.name === 'length') { return FunctionalFuncs.getArrayRefLen(this.module, objRef); } throw Error(`unhandle Array field get: ${shapeMember.name}`); } case ValueTypeKind.STRING: { const objRef = this.wasmExprGen(owner); if (shapeMember.name === 'length') { return FunctionalFuncs.getStringRefLen(this.module, objRef); } throw Error(`unhandle String field get: ${shapeMember.name}`); } default: throw new UnimplementError('Unimplement wasmObjFieldGet'); } } getIndirectValueRef( objRef: binaryen.ExpressionRef, indexRef: binaryen.ExpressionRef, flagRef: binaryen.ExpressionRef, valueType: ValueType, ) { const propValueTypeRef = this.wasmTypeGen.getWASMValueType(valueType); const propTypeRef = this.wasmTypeGen.getWASMType(valueType); let realValueRef: binaryen.ExpressionRef; /* just for providing get_indirect path, not used in real custom envionment */ if (propTypeRef === binaryen.i64) { return this.module.call( structdyn.StructDyn.struct_get_indirect_i64, [objRef, indexRef], binaryen.i64, ); } else if (propTypeRef === binaryen.f32) { return this.module.call( structdyn.StructDyn.struct_get_indirect_f32, [objRef, indexRef], binaryen.f32, ); } switch (valueType.kind) { case ValueTypeKind.INT: case ValueTypeKind.BOOLEAN: { realValueRef = this.module.call( structdyn.StructDyn.struct_get_indirect_i32, [objRef, indexRef], binaryen.i32, ); break; } case ValueTypeKind.WASM_F32: { realValueRef = this.module.call( structdyn.StructDyn.struct_get_indirect_f32, [objRef, indexRef], binaryen.f32, ); break; } case ValueTypeKind.WASM_I64: { realValueRef = this.module.call( structdyn.StructDyn.struct_get_indirect_i64, [objRef, indexRef], binaryen.i64, ); break; } case ValueTypeKind.NUMBER: { realValueRef = this.module.call( structdyn.StructDyn.struct_get_indirect_f64, [objRef, indexRef], binaryen.f64, ); break; } case ValueTypeKind.FUNCTION: { /* if is field, get from instance, cast to closureRef */ const closureRef = this.module.call( structdyn.StructDyn.struct_get_indirect_anyref, [objRef, indexRef], binaryen.anyref, ); const isFieldTrue = binaryenCAPI._BinaryenRefCast( this.module.ptr, closureRef, propValueTypeRef, ); /* if is method, get vtable firstly, then get method from vtable, finally box method to closureRef */ const vtableRef = this.module.call( structdyn.StructDyn.struct_get_indirect_anyref, [objRef, this.module.i32.const(0)], binaryen.anyref, ); const funcRef = this.module.call( structdyn.StructDyn.struct_get_indirect_funcref, [vtableRef, indexRef], binaryen.funcref, ); const isMethodTrue = this.getClosureOfFunc( binaryenCAPI._BinaryenRefCast( this.module.ptr, funcRef, propTypeRef, ), valueType as FunctionType, objRef, ); realValueRef = this.module.if( FunctionalFuncs.isFieldFlag(this.module, flagRef), isFieldTrue, this.module.if( FunctionalFuncs.isMethodFlag(this.module, flagRef), isMethodTrue, this.module.unreachable(), ), ); break; } default: { realValueRef = this.module.call( structdyn.StructDyn.struct_get_indirect_anyref, [objRef, indexRef], binaryen.anyref, ); if (this.wasmTypeGen.hasHeapType(valueType)) { realValueRef = binaryenCAPI._BinaryenRefCast( this.module.ptr, realValueRef, propValueTypeRef, ); } break; } } return realValueRef; } private dynGetInfcProperty( objRef: binaryen.ExpressionRef, flagAndIndexRef: binaryen.ExpressionRef, valueType: ValueType, isOptional: boolean, propTypeIdRef: binaryen.ExpressionRef, ) { const valueTypeRef = this.wasmTypeGen.getWASMValueType(valueType); const indexRef = FunctionalFuncs.getPropIndexFromObj( this.module, flagAndIndexRef, ); const flagRef = FunctionalFuncs.getPropFlagFromObj( this.module, flagAndIndexRef, ); const infcPropTypeIdRef = this.module.i32.const( FunctionalFuncs.getPredefinedTypeId(valueType), ); const ifPropTypeIdCompatible = FunctionalFuncs.isPropTypeIdCompatible( this.module, infcPropTypeIdRef, propTypeIdRef, ); let ifPropTypeIdEqualTrue = this.getIndirectValueRef( objRef, indexRef, flagRef, valueType, ); if (isOptional) { ifPropTypeIdEqualTrue = this.module.if( FunctionalFuncs.isPropertyUnExist(this.module, flagAndIndexRef), FunctionalFuncs.generateDynUndefined(this.module), FunctionalFuncs.boxNonLiteralToAny( this.module, ifPropTypeIdEqualTrue, valueType.kind, ), ); } /* Hint for benchmark: here we add a comparation between two prop types, and an additional call for wasm function is called */ /* TODO: reduce the additional call */ /* if two prop type id is equal, we can use the prop type information get from compiler time */ let ifPropTypeIdEqualFalse: binaryen.ExpressionRef; if ( valueType instanceof UnionType || (valueType instanceof FunctionType && isOptional) ) { /* For union type, we can parse type one by one to avoid runtime getting */ /* For type T? (optional): * - when T is function type, it's regarded as T * - otherwise it's regarded as union (T | undefined) */ ifPropTypeIdEqualFalse = this.dynGetInfcUnionProperty( objRef, flagAndIndexRef, valueType, propTypeIdRef, isOptional, ); } else { const anyTypedProperty = this.module.call( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.getPropertyIfTypeIdMismatch, ), [flagAndIndexRef, infcPropTypeIdRef, propTypeIdRef, objRef], binaryen.anyref, ); ifPropTypeIdEqualFalse = FunctionalFuncs.unboxAny( this.module, anyTypedProperty, valueType.kind, valueTypeRef, ); } return this.module.if( ifPropTypeIdCompatible, ifPropTypeIdEqualTrue, ifPropTypeIdEqualFalse, ); } private dynGetInfcUnionProperty( objRef: binaryen.ExpressionRef, flagAndIndexRef: binaryen.ExpressionRef, valueType: UnionType | FunctionType, propertyTypeIdRef: binaryen.ExpressionRef, isOptional: boolean, ) { /** * For const foo: A | B | undefined * if A has been parsed, no need to parse B, because both they are class types, * here uses a Set to record the parsed types. */ const parsedTypes: Set = new Set(); let types: ValueType[] = [valueType]; if (valueType instanceof UnionType) { types = Array.from(valueType.types); } const ifExpr = this.dynGetInfcUnionPropertyHelper( objRef, flagAndIndexRef, types[0], propertyTypeIdRef, ); let curIfExpr = ifExpr; parsedTypes.add(types[0].kind); for (let i = 1; i < types.length; i++) { if (parsedTypes.has(types[i].kind)) { continue; } const ifExprOfIth = this.dynGetInfcUnionPropertyHelper( objRef, flagAndIndexRef, types[i], propertyTypeIdRef, ); binaryenCAPI._BinaryenIfSetIfFalse(curIfExpr, ifExprOfIth); curIfExpr = ifExprOfIth; parsedTypes.add(types[i].kind); } if (isOptional) { binaryenCAPI._BinaryenIfSetIfFalse( curIfExpr, FunctionalFuncs.generateDynUndefined(this.module), ); } else { binaryenCAPI._BinaryenIfSetIfFalse( curIfExpr, this.module.unreachable(), ); } /* Use a block to wrap statement to make sure binaryen will treat the result type as anyref */ return this.module.block( null, [ this.module.if( FunctionalFuncs.isPropertyUnExist( this.module, flagAndIndexRef, ), FunctionalFuncs.generateDynUndefined(this.module), ifExpr, ), ], binaryen.anyref, ); } private dynGetInfcUnionPropertyHelper( objRef: binaryen.ExpressionRef, flagAndIndexRef: binaryen.ExpressionRef, valueType: ValueType, propTypeIdRef: binaryen.ExpressionRef, ) { const indexRef = FunctionalFuncs.getPropIndexFromObj( this.module, flagAndIndexRef, ); const flagRef = FunctionalFuncs.getPropFlagFromObj( this.module, flagAndIndexRef, ); let condition: binaryen.ExpressionRef; const valueTypeId = FunctionalFuncs.getPredefinedTypeId(valueType); if (valueTypeId >= PredefinedTypeId.CUSTOM_TYPE_BEGIN) { condition = FunctionalFuncs.isPropTypeIdIsObject( this.module, propTypeIdRef, ); } else { condition = FunctionalFuncs.isPropTypeIdCompatible( this.module, this.module.i32.const(valueTypeId), propTypeIdRef, ); } const realValueRef = this.getIndirectValueRef( objRef, indexRef, flagRef, valueType, ); const anyTypedRealValueRef = FunctionalFuncs.boxNonLiteralToAny( this.module, realValueRef, valueType.kind, ); return this.module.if(condition, anyTypedRealValueRef); } setIndirectValueRef( objRef: binaryen.ExpressionRef, indexRef: binaryen.ExpressionRef, flagRef: binaryen.ExpressionRef, valueType: ValueType, valueRef: binaryen.ExpressionRef, ) { const propValueTypeRef = this.wasmTypeGen.getWASMValueType(valueType); const propTypeRef = this.wasmTypeGen.getWASMType(valueType); let res: binaryen.ExpressionRef; /* just for providing get_indirect path, not used in real custom envionment */ if (propTypeRef === binaryen.i64) { return this.module.call( structdyn.StructDyn.struct_set_indirect_i64, [objRef, indexRef, valueRef], binaryen.none, ); } else if (propTypeRef === binaryen.f32) { return this.module.call( structdyn.StructDyn.struct_set_indirect_f32, [objRef, indexRef, valueRef], binaryen.none, ); } switch (valueType.kind) { case ValueTypeKind.INT: case ValueTypeKind.BOOLEAN: { res = this.module.call( structdyn.StructDyn.struct_set_indirect_i32, [objRef, indexRef, valueRef], binaryen.none, ); break; } case ValueTypeKind.WASM_F32: { res = this.module.call( structdyn.StructDyn.struct_set_indirect_f32, [objRef, indexRef, valueRef], binaryen.none, ); break; } case ValueTypeKind.WASM_I64: { res = this.module.call( structdyn.StructDyn.struct_set_indirect_i64, [objRef, indexRef, valueRef], binaryen.none, ); break; } case ValueTypeKind.NUMBER: { res = this.module.call( structdyn.StructDyn.struct_set_indirect_f64, [objRef, indexRef, valueRef], binaryen.none, ); break; } case ValueTypeKind.FUNCTION: { /* if is field, just set method to instance directly, we should ensure that the value is a closureRef */ const isFieldTrue = this.module.call( structdyn.StructDyn.struct_set_indirect_anyref, [objRef, indexRef, valueRef], binaryen.none, ); /* if is method, get vtable firstly, then set method to vtable, we should unbox method from closureRef to funcRef */ const vtableRef = this.module.call( structdyn.StructDyn.struct_get_indirect_anyref, [objRef, this.module.i32.const(0)], binaryen.anyref, ); const isMethodTrue = this.module.call( structdyn.StructDyn.struct_set_indirect_funcref, [ vtableRef, indexRef, this.getFuncRefFromClosure(valueRef, propValueTypeRef), ], binaryen.none, ); res = this.module.if( FunctionalFuncs.isFieldFlag(this.module, flagRef), isFieldTrue, this.module.if( FunctionalFuncs.isMethodFlag(this.module, flagRef), isMethodTrue, this.module.unreachable(), ), ); break; } default: { res = this.module.call( structdyn.StructDyn.struct_set_indirect_anyref, [objRef, indexRef, valueRef], binaryen.none, ); break; } } return res; } private dynSetInfcProperty( objRef: binaryen.ExpressionRef, flagAndIndexRef: binaryen.ExpressionRef, valueType: ValueType, isOptional: boolean, propTypeIdRef: binaryen.ExpressionRef, valueRef: binaryen.ExpressionRef, ) { const indexRef = FunctionalFuncs.getPropIndexFromObj( this.module, flagAndIndexRef, ); const flagRef = FunctionalFuncs.getPropFlagFromObj( this.module, flagAndIndexRef, ); const targetValueTypeIdRef = this.module.i32.const( FunctionalFuncs.getPredefinedTypeId(valueType), ); const ifPropTypeIdCompatible = FunctionalFuncs.isPropTypeIdCompatible( this.module, propTypeIdRef, targetValueTypeIdRef, ); const anyTypedProperty = FunctionalFuncs.boxNonLiteralToAny( this.module, valueRef, valueType.kind, ); let ifPropTypeIdEqualTrue: binaryen.ExpressionRef = this.setIndirectValueRef( objRef, indexRef, flagRef, valueType, valueRef, ); if (isOptional) { ifPropTypeIdEqualTrue = this.module.if( FunctionalFuncs.isPropertyUnExist(this.module, flagAndIndexRef), this.module.unreachable(), this.module.call( structdyn.StructDyn.struct_set_indirect_anyref, [objRef, indexRef, anyTypedProperty], binaryen.none, ), ); } let ifPropTypeIdEqualFalse: binaryen.ExpressionRef; if ( valueType instanceof UnionType || (valueType instanceof FunctionType && isOptional) ) { ifPropTypeIdEqualFalse = this.dynSetInfcUnionProperty( objRef, flagAndIndexRef, valueType, propTypeIdRef, isOptional, valueRef, ); } else { ifPropTypeIdEqualFalse = this.module.call( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.setPropertyIfTypeIdMismatch, ), [ flagAndIndexRef, targetValueTypeIdRef, propTypeIdRef, objRef, anyTypedProperty, ], binaryen.none, ); } return this.module.if( ifPropTypeIdCompatible, ifPropTypeIdEqualTrue, ifPropTypeIdEqualFalse, ); } private dynSetInfcUnionProperty( objRef: binaryen.ExpressionRef, flagAndIndexRef: binaryen.ExpressionRef, valueType: UnionType | FunctionType, propTypeIdRef: binaryen.ExpressionRef, isOptional: boolean, valueRef: binaryen.ExpressionRef, ) { const parsedTypes: Set = new Set(); let types: ValueType[] = [valueType]; if (valueType instanceof UnionType) { types = Array.from(valueType.types); } const ifExpr = this.dynSetInfcUnionPropertyHelper( objRef, flagAndIndexRef, types[0], propTypeIdRef, valueRef, ); parsedTypes.add(types[0].kind); let curIfExpr = ifExpr; for (let i = 1; i < types.length; i++) { if (parsedTypes.has(types[i].kind)) { continue; } const ifExprOfIth = this.dynSetInfcUnionPropertyHelper( objRef, flagAndIndexRef, types[i], propTypeIdRef, valueRef, ); binaryenCAPI._BinaryenIfSetIfFalse(curIfExpr, ifExprOfIth); curIfExpr = ifExprOfIth; parsedTypes.add(types[i].kind); } if (isOptional) { const indexRef = FunctionalFuncs.getPropIndexFromObj( this.module, flagAndIndexRef, ); binaryenCAPI._BinaryenIfSetIfFalse( curIfExpr, this.module.call( structdyn.StructDyn.struct_set_indirect_anyref, [objRef, indexRef, valueRef], binaryen.none, ), ); } else { binaryenCAPI._BinaryenIfSetIfFalse( curIfExpr, this.module.unreachable(), ); } return this.module.if( FunctionalFuncs.isPropertyUnExist(this.module, flagAndIndexRef), this.module.unreachable(), ifExpr, ); } private dynSetInfcUnionPropertyHelper( objRef: binaryen.ExpressionRef, flagAndIndexRef: binaryen.ExpressionRef, valueType: ValueType, propTypeIdRef: binaryen.ExpressionRef, valueRef: binaryen.ExpressionRef, ) { const indexRef = FunctionalFuncs.getPropIndexFromObj( this.module, flagAndIndexRef, ); const flagRef = FunctionalFuncs.getPropFlagFromObj( this.module, flagAndIndexRef, ); let condition: binaryen.ExpressionRef; const valueTypeId = FunctionalFuncs.getPredefinedTypeId(valueType); if (valueTypeId >= PredefinedTypeId.CUSTOM_TYPE_BEGIN) { condition = FunctionalFuncs.isPropTypeIdIsObject( this.module, propTypeIdRef, ); } else { condition = FunctionalFuncs.isPropTypeIdCompatible( this.module, propTypeIdRef, this.module.i32.const(valueTypeId), ); } const propValueTypeRef = this.wasmTypeGen.getWASMValueType(valueType); const typedValueRef = FunctionalFuncs.unboxAny( this.module, valueRef, valueType.kind, propValueTypeRef, ); const res = this.setIndirectValueRef( objRef, indexRef, flagRef, valueType, typedValueRef, ); return this.module.if(condition, res); } private wasmDirectGetter(value: DirectGetterValue) { const owner = value.owner as VarValue; const returnTypeRef = this.wasmTypeGen.getWASMType(value.type); const objRef = this.wasmExprGen(owner); const methodMangledName = (value.getter as any).index as string; return this.module.call( methodMangledName, [this.wasmCompiler.emptyRef, objRef], returnTypeRef, ); } private wasmDirectSetter(value: DirectSetterValue) { const owner = value.owner as VarValue; const returnTypeRef = this.wasmTypeGen.getWASMType(value.type); const objRef = this.wasmExprGen(owner); const methodMangledName = (value.setter as any).index as string; return this.module.call( methodMangledName, [ this.wasmCompiler.emptyRef, objRef, this.wasmExprGen(value.value!), ], binaryen.none, ); } private getTruthIdx( meta: ObjectDescription, member: MemberDescription, isSetter = false, ) { /* The index provided by semantic tree is unrealiable, we must recompute it */ let valueIdx = 0; if (member.type === MemberType.FIELD) { valueIdx = this.fixFieldIndex(meta, member); } else { valueIdx = this.fixVtableIndex(meta, member, isSetter); } return valueIdx; } private getMemberByName(meta: ObjectDescription, propName: string) { let foundMember: MemberDescription | undefined = undefined; for (const member of meta.members) { if (member.name === propName) { foundMember = member; break; } } if (!foundMember) { throw Error(`not found ${propName} in getMemberByName`); } return foundMember; } private wasmDynamicGet(value: DynamicGetValue) { const owner = value.owner; const propName = value.name; const propNameRef = this.getStringOffset(propName); switch (owner.type.kind) { case ValueTypeKind.ANY: { const ownValueRef = this.wasmExprGen(owner); return FunctionalFuncs.getDynObjProp( this.module, ownValueRef, propNameRef, ); } case ValueTypeKind.UNION: { const ownValueRef = this.wasmExprGen(owner); const dynamicGetProp = FunctionalFuncs.getDynObjProp( this.module, ownValueRef, propNameRef, ); if (FunctionalFuncs.isUnionWithUndefined(owner.type)) { const isNonUndefined = FunctionalFuncs.generateCondition( this.module, ownValueRef, ValueTypeKind.UNION, ); const staticType = FunctionalFuncs.getStaticType( owner.type, ); const wasmStaticType = this.wasmTypeGen.getWASMValueType(staticType); let ownerStaticValueRef = FunctionalFuncs.unboxAny( this.module, ownValueRef, staticType.kind, wasmStaticType, ); ownerStaticValueRef = binaryenCAPI._BinaryenRefCast( this.module.ptr, ownerStaticValueRef, wasmStaticType, ); let propValueRef: binaryen.ExpressionRef; let propType: ValueType; if ( staticType.kind === ValueTypeKind.STRING && propName === 'length' ) { propValueRef = FunctionalFuncs.getStringRefLen( this.module, ownerStaticValueRef, ); propType = Primitive.Number; } else if ( staticType.kind === ValueTypeKind.ARRAY && propName === 'length' ) { propValueRef = FunctionalFuncs.getArrayRefLen( this.module, ownerStaticValueRef, ); propType = Primitive.Number; } else if (staticType instanceof ObjectType) { const member = this.getMemberByName( staticType.meta, propName, ); propValueRef = this.getInstProperty( ownerStaticValueRef, staticType, staticType.meta, member, ); propType = member.valueType; } else { return dynamicGetProp; } propValueRef = propType instanceof PrimitiveType || propType instanceof UnionType ? FunctionalFuncs.boxBaseTypeToAny( this.module, propValueRef, propType.kind, ) : FunctionalFuncs.generateDynExtrefByTypeKind( this.module, propValueRef, propType.kind, ); return this.module.if( isNonUndefined, propValueRef, FunctionalFuncs.generateDynUndefined(this.module), ); } else { return dynamicGetProp; } } case ValueTypeKind.OBJECT: { const meta = (owner.type as ObjectType).meta; const foundMember = this.getMemberByName(meta, propName); const propertyIdx = this.getTruthIdx(meta, foundMember); if (meta.isObjectClass) { /* class A; A.yy */ /* workaround: class get static field is a ShapeGetValue, this can be deleted later */ return this.getClassStaticField( foundMember, meta, owner.type as ObjectType, ); } else { /* let a: A = xx; a.yy */ /* let o = {xx}; o.yy */ const ownValueRef = this.wasmExprGen(owner); return this.getObjProperty( foundMember, owner.type, ownValueRef, propertyIdx, ); } } case ValueTypeKind.WASM_ARRAY: case ValueTypeKind.ARRAY: { if (propName === 'length') { const ownValueRef = this.wasmExprGen(owner); return FunctionalFuncs.getArrayRefLen( this.module, ownValueRef, owner, ); } throw Error(`unhandle Array field get: ${propName}`); } case ValueTypeKind.STRING: { if (propName === 'length') { const ownValueRef = this.wasmExprGen(owner); return FunctionalFuncs.getStringRefLen( this.module, ownValueRef, ); } throw Error(`unhandle String field get: ${propName}`); } case ValueTypeKind.WASM_STRUCT: case ValueTypeKind.TUPLE: { if (propName === 'length') { const fields = owner.type instanceof TupleType ? owner.type.elements : (owner.type).tupleType.elements; return this.module.f64.const(fields.length); } throw Error(`unhandle Array field get: ${propName}`); } default: throw Error(`wasmDynamicGet: ${value}`); } } private wasmDynamicSet(value: DynamicSetValue) { const oriValue = value.value!; const oriValueRef = this.wasmExprGen(oriValue); const ownVarDecl = (value.owner as VarValue).ref as VarDeclareNode; const ownValueRef = this.wasmExprGen(value.owner); switch (ownVarDecl.type.kind) { case ValueTypeKind.ANY: { /* set any prop */ const propNameRef = this.getStringOffset(value.name); const initValueToAnyRef = FunctionalFuncs.boxToAny( this.module, oriValueRef, oriValue, ); return this.module.drop( FunctionalFuncs.setDynObjProp( this.module, ownValueRef, propNameRef, initValueToAnyRef, ), ); } case ValueTypeKind.OBJECT: { const objType = ownVarDecl.type as ObjectType; const typeMeta = objType.meta; const typeMember = this.getMemberByName(typeMeta, value.name); return this.setInstProperty( ownValueRef, objType, typeMeta, typeMember, oriValue, ); } default: throw Error(`wasmDynamicSet: ${value}`); } } private wasmNewLiteralArray(value: NewLiteralArrayValue) { switch (value.type.kind) { case ValueTypeKind.ARRAY: case ValueTypeKind.WASM_ARRAY: { return this.wasmElemsToArr(value.initValues, value.type); } case ValueTypeKind.TUPLE: case ValueTypeKind.WASM_STRUCT: { const fieldRefs: binaryen.ExpressionRef[] = []; for (const field of value.initValues) { fieldRefs.push(this.wasmExprGen(field)); } const heapTypeRef = this.wasmTypeGen.getWASMHeapType( value.type, ); return binaryenCAPI._BinaryenStructNew( this.module.ptr, arrayToPtr(fieldRefs).ptr, value.initValues.length, heapTypeRef, ); } default: { throw new UnimplementError( `unimplement type kind ${value.type.kind} in wasmNewLiteralArray`, ); } } } private wasmNewArray(value: NewArrayValue | NewArrayLenValue) { let arrayRef: binaryen.ExpressionRef; let arraySizeRef: binaryen.ExpressionRef; const arrayType: ArrayType = value.type instanceof ArrayType ? value.type : (value.type).arrayType; const arrayHeapType = this.wasmTypeGen.getWASMArrayOriHeapType(arrayType); const arrayStructHeapType = this.wasmTypeGen.getWASMHeapType(arrayType); if (value instanceof NewArrayValue) { const arrayLen = value.parameters.length; const elemRefs: binaryen.ExpressionRef[] = []; for (let i = 0; i < arrayLen; i++) { const elemRef = this.wasmExprGen(value.parameters[i]); elemRefs.push(elemRef); } arrayRef = binaryenCAPI._BinaryenArrayNewFixed( this.module.ptr, arrayHeapType, arrayToPtr(elemRefs).ptr, arrayLen, ); arraySizeRef = this.module.i32.const(arrayLen); } else if (value instanceof NewArrayLenValue) { const arrayInit = this.getArrayInitFromArrayType(arrayType); arraySizeRef = FunctionalFuncs.convertTypeToI32( this.module, this.wasmExprGen(value.len), ); arrayRef = binaryenCAPI._BinaryenArrayNew( this.module.ptr, arrayHeapType, arraySizeRef, arrayInit, ); } const arrayStructRef = binaryenCAPI._BinaryenStructNew( this.module.ptr, arrayToPtr([arrayRef!, arraySizeRef!]).ptr, 2, arrayStructHeapType, ); const res = value.type instanceof ArrayType ? arrayStructRef : arrayRef!; return res; } private elemOp(value: ElementGetValue | ElementSetValue) { const ownerRef = this.wasmExprGen(value.owner); let valueType = value.type; if (value.kind === SemanticsValueKind.OBJECT_KEY_SET) { valueType = (value as ElementSetValue).value!.type; } const indexStrRef = this.wasmExprGen(value.index); const propertyOffset = this.encodeStringrefToLinearMemory(indexStrRef); /* invoke get_indirect/set_indirect to set prop value to obj */ const metaRef = FunctionalFuncs.getWASMObjectMeta( this.module, ownerRef, ); const flag = ItableFlag.ALL; const flagAndIndexRef = this.getPropFlagAndIdxRefFromObj( metaRef, propertyOffset, flag, ); const propTypeIdRef = this.getPropTypeIdRefFromObj( metaRef, propertyOffset, flag, ); let elemOperation: binaryen.ExpressionRef; if (value.kind === SemanticsValueKind.OBJECT_KEY_SET) { elemOperation = this.dynSetInfcProperty( ownerRef, flagAndIndexRef, valueType, false, propTypeIdRef, this.wasmExprGen((value as ElementSetValue).value!), ); } else { elemOperation = this.dynGetInfcProperty( ownerRef, flagAndIndexRef, valueType, false, propTypeIdRef, ); } return elemOperation; } private wasmElemGet(value: ElementGetValue) { const owner = value.owner; const ownerType = owner.type; switch (ownerType.kind) { case ValueTypeKind.ARRAY: case ValueTypeKind.WASM_ARRAY: { const ownerRef = this.wasmExprGen(owner); const idxI32Ref = FunctionalFuncs.convertTypeToI32( this.module, this.wasmExprGen(value.index), ); const ownerTypeRef = this.wasmTypeGen.getWASMType(ownerType); const ownerHeapTypeRef = this.wasmTypeGen.getWASMHeapType(ownerType); return FunctionalFuncs.getArrayElemByIdx( this.module, ownerTypeRef, ownerRef, ownerHeapTypeRef, idxI32Ref, ownerType.kind === ValueTypeKind.ARRAY ? false : true, ); } /* workaround: sometimes semantic tree will treat array as any * test case: array_class2 in array_push.ts * However, this case need to reserve. */ case ValueTypeKind.ANY: { const ownerRef = this.wasmExprGen(owner); const idxRef = this.wasmExprGen(value.index); const idxI32Ref = FunctionalFuncs.convertTypeToI32( this.module, idxRef, ); switch (value.index.type.kind) { case ValueTypeKind.NUMBER: case ValueTypeKind.INT: { const elemGetInArrRef = FunctionalFuncs.getDynArrElem( this.module, ownerRef, idxI32Ref, ); return elemGetInArrRef; } default: { const propertyOffset = this.encodeStringrefToLinearMemory(idxRef); const elemGetInObjRef = FunctionalFuncs.getDynObjProp( this.module, ownerRef, propertyOffset, ); return elemGetInObjRef; } } } case ValueTypeKind.STRING: { const ownerRef = this.wasmExprGen(owner); const idxF64Ref = FunctionalFuncs.convertTypeToF64( this.module, this.wasmExprGen(value.index), ); if (getConfig().enableStringRef) { const invokeArgs = [ new CastValue( SemanticsValueKind.VALUE_CAST_ANY, owner.type, owner, ) as SemanticsValue, value.index, ]; return this.module.call( dyntype.dyntype_to_string, [ FunctionalFuncs.getDynContextRef(this.module), this.dyntypeInvoke('charAt', invokeArgs), ], binaryenCAPI._BinaryenTypeStringref(), ); } return this.module.call( getBuiltInFuncName(BuiltinNames.stringcharAtFuncName), [this.wasmCompiler.emptyRef, ownerRef, idxF64Ref], stringTypeInfo.typeRef, ); } case ValueTypeKind.OBJECT: { return this.elemOp(value); } case ValueTypeKind.TUPLE: case ValueTypeKind.WASM_STRUCT: { const ownerRef = this.wasmExprGen(owner); const ownerHeapTypeRef = this.wasmTypeGen.getWASMHeapType(ownerType); const fields = ownerType instanceof TupleType ? ownerType.elements : (ownerType as WASMStructType).tupleType.elements; if (value.index instanceof LiteralValue) { /* If we can get constant, then invoke BinaryenStructGet */ const idx = value.index.value as number; return binaryenCAPI._BinaryenStructGet( this.module.ptr, idx, ownerRef, ownerHeapTypeRef, false, ); } else { if (ownerType instanceof WASMStructType) { throw new Error( `index must be a constant in WASMStruct getting`, ); } /* If we can not get constant as index, then invoke struct_get_indirect */ const idxI32Ref = FunctionalFuncs.convertTypeToI32( this.module, this.wasmExprGen(value.index), ); const wasmRawArrayLocal = this.wasmCompiler.currentFuncCtx!.insertTmpVar( i32ArrayTypeInfo.typeRef, ); const newWasmRawArrayOp = this.module.local.set( wasmRawArrayLocal.index, binaryenCAPI._BinaryenArrayNew( this.module.ptr, i32ArrayTypeInfo.heapTypeRef, this.module.i32.const(fields.length), this.module.i32.const(0), ), ); this.wasmCompiler.currentFuncCtx!.insert(newWasmRawArrayOp); for (let i = 0; i < fields.length; i++) { const fieldType = fields[i]; const predefinedTypeId = FunctionalFuncs.getPredefinedTypeId(fieldType); this.wasmCompiler.currentFuncCtx!.insert( binaryenCAPI._BinaryenArraySet( this.module.ptr, this.module.local.get( wasmRawArrayLocal.index, wasmRawArrayLocal.type, ), this.module.i32.const(i), this.module.i32.const(predefinedTypeId), ), ); } const res = this.module.call( BuiltinNames.getTupleField, [ this.module.local.get( wasmRawArrayLocal.index, wasmRawArrayLocal.type, ), idxI32Ref, ownerRef, ], binaryen.anyref, ); return res; } } default: throw Error(`wasmIdxGet: ${value}`); } } private wasmElemSet(value: ElementSetValue) { const owner = value.owner as VarValue; const ownerType = owner.type; switch (ownerType.kind) { case ValueTypeKind.ARRAY: case ValueTypeKind.WASM_ARRAY: { const ownerRef = this.wasmExprGen(owner); const idxI32Ref = FunctionalFuncs.convertTypeToI32( this.module, this.wasmExprGen(value.index), ); const targetValueRef = this.wasmExprGen(value.value!); const ownerHeapTypeRef = this.wasmTypeGen.getWASMHeapType(ownerType); return FunctionalFuncs.setArrayElemByIdx( this.module, ownerRef, ownerHeapTypeRef, idxI32Ref, targetValueRef, ownerType.kind === ValueTypeKind.ARRAY ? false : true, ); } case ValueTypeKind.ANY: { const ownerRef = this.wasmExprGen(owner); const idxRef = this.wasmExprGen(value.index); const idxI32Ref = FunctionalFuncs.convertTypeToI32( this.module, idxRef, ); const targetValueRef = this.wasmExprGen(value.value!); switch (value.index.type.kind) { case ValueTypeKind.NUMBER: case ValueTypeKind.INT: { const elemSetInArrRef = FunctionalFuncs.setDynArrElem( this.module, ownerRef, idxI32Ref, targetValueRef, ); return elemSetInArrRef; } default: { const propertyOffset = this.encodeStringrefToLinearMemory(idxRef); const elemSetInObjRef = FunctionalFuncs.setDynObjProp( this.module, ownerRef, propertyOffset, targetValueRef, ); return elemSetInObjRef; } } } case ValueTypeKind.OBJECT: { return this.elemOp(value); } case ValueTypeKind.TUPLE: case ValueTypeKind.WASM_STRUCT: { const ownerRef = this.wasmExprGen(owner); /* TODO: _BinaryenStructSet only accept ts number as index, not binaryen.ExpressionRef as index */ const idxI32Ref = FunctionalFuncs.convertTypeToI32( this.module, this.wasmExprGen(value.index), ); const targetValueRef = this.wasmExprGen(value.value!); let idx = 0; if (value.index instanceof LiteralValue) { idx = value.index.value as number; } else { throw new UnimplementError( `not sure how to convert idxI32Ref to a regular index yet in wasmElemSet`, ); } return binaryenCAPI._BinaryenStructSet( this.module.ptr, idx, ownerRef, targetValueRef, ); } default: throw Error(`wasmIdxSet: ${value}`); } } private wasmBlockValue(value: BlockValue) { const blockArray: binaryen.ExpressionRef[] = []; for (const blockValue of value.values) { blockArray.push(this.wasmExprGen(blockValue)); } return this.module.block( value.label, blockArray, this.wasmTypeGen.getWASMType(value.type), ); } private wasmBlockIFValue(value: BlockBranchIfValue) { const oriCondRef = this.wasmExprGen(value.condition); const targetRef = this.wasmExprGen(value.target); const isTrueBranch = value.trueBranch; let condRef: binaryen.ExpressionRef; if (isTrueBranch) { condRef = oriCondRef; } else { condRef = this.module.i32.eqz(oriCondRef); } return this.module.if(condRef, targetRef); } private wasmBlockBranchValue(value: BlockBranchValue) { const targetLabel = value.target.label; return this.module.br(targetLabel); } private getArrayInitFromArrayType( arrayType: ArrayType, ): binaryen.ExpressionRef { const module = this.module; const elemType = arrayType.element; switch (elemType.kind) { case ValueTypeKind.NUMBER: { return module.f64.const(0); } case ValueTypeKind.STRING: { if (getConfig().enableStringRef) { return this.createStringRef(''); } else { return FunctionalFuncs.generateStringForStructArrayStr( this.module, '', ); } } case ValueTypeKind.WASM_I64: { return module.i64.const(0, 0); } case ValueTypeKind.WASM_F32: { return module.f32.const(0); } case ValueTypeKind.INT: case ValueTypeKind.BOOLEAN: { return module.i32.const(0); } case ValueTypeKind.FUNCTION: { return binaryenCAPI._BinaryenRefNull( module.ptr, builtinClosureType.typeRef, ); } default: { return binaryenCAPI._BinaryenRefNull( module.ptr, this.wasmTypeGen.getWASMType(elemType), ); } } } private generateDynamicArg(args?: Array) { const restArgs = args ? args.map((a) => { return FunctionalFuncs.boxToAny( this.module, this.wasmExprGen(a), a, ); }) : []; const tmpArgVar = this.wasmCompiler.currentFuncCtx!.insertTmpVar( dyntype.dyn_value_t, ); const createDynObjOps: binaryen.ExpressionRef[] = []; const setDynamicArg = this.module.local.set( tmpArgVar.index, FunctionalFuncs.generateDynArray( this.module, this.module.i32.const(restArgs.length), ), ); createDynObjOps.push(setDynamicArg); for (let i = 0; i < restArgs.length; i++) { createDynObjOps.push( FunctionalFuncs.setDynArrElem( this.module, this.module.local.get(tmpArgVar.index, dyntype.dyn_value_t), this.module.i32.const(i), restArgs[i], ), ); } this.wasmCompiler.currentFuncCtx!.insert( this.module.block(null, createDynObjOps), ); return this.module.local.get(tmpArgVar.index, dyntype.dyn_value_t); } /** the dynamic object will fallback to libdyntype */ private dyntypeInvoke( name: string, args: Array, isNew = false, ): binaryen.ExpressionRef { const namePointer = this.wasmCompiler.generateRawString(name); const thisArg = !isNew ? this.wasmExprGen(args.splice(0, 1)[0]) : undefined; const dynamicArg = this.generateDynamicArg(args); const finalArgs = [ FunctionalFuncs.getDynContextRef(this.module), this.module.i32.const(namePointer), ]; if (!isNew) { finalArgs.push(thisArg!); } finalArgs.push(dynamicArg); const res = this.module.call( isNew ? dyntype.dyntype_new_object_with_class : dyntype.dyntype_invoke, finalArgs, dyntype.dyn_value_t, ); return res; } private wasmTypeof(value: TypeofValue): binaryen.ExpressionRef { const expr = this.wasmExprGen(value.value); const res = this.module.call( dyntype.dyntype_typeof, [ this.module.global.get( dyntype.dyntype_context, binaryen.anyref, ), expr, ], stringTypeInfo.typeRef, ); return res; } private wasmTemplateExpr(value: TemplateExprValue): binaryen.ExpressionRef { const head = this.wasmExprGen(value.head); // create a string array; const follows = value.follows; const followsExprRef: binaryen.ExpressionRef[] = []; const stringArrayType = getConfig().enableStringRef ? stringrefArrayTypeInfo : stringArrayTypeInfo; const stringArrayStructType = getConfig().enableStringRef ? stringrefArrayStructTypeInfo : stringArrayStructTypeInfo; for (const follow of follows) { followsExprRef.push(this.wasmExprGen(follow)); } const arrayValue = binaryenCAPI._BinaryenArrayNewFixed( this.module.ptr, stringArrayType.heapTypeRef, arrayToPtr(followsExprRef).ptr, followsExprRef.length, ); const arrayStructValue = binaryenCAPI._BinaryenStructNew( this.module.ptr, arrayToPtr([arrayValue, this.module.i32.const(follows.length)]).ptr, 2, stringArrayStructType.heapTypeRef, ); return this.module.call( UtilFuncs.getFuncName( BuiltinNames.builtinModuleName, BuiltinNames.stringConcatFuncName, ), [ this.module.ref.null(stringArrayType.typeRef), head, arrayStructValue, ], stringTypeInfo.typeRef, ); } private wasmToString(value: ToStringValue): binaryen.ExpressionRef { const expr = this.wasmExprGen(value.value); const boxedExpr = FunctionalFuncs.boxToAny( this.module, expr, value.value, ); const res = this.module.call( dyntype.dyntype_toString, [ this.module.global.get( dyntype.dyntype_context, binaryen.anyref, ), boxedExpr, ], getConfig().enableStringRef ? binaryenCAPI._BinaryenTypeStringref() : stringTypeInfo.typeRef, ); return res; } private wasmObjTypeCastToAny(value: CastValue) { const fromValue = value.value; const fromType = fromValue.type; const fromObjType = fromType as ObjectType; /* Workaround: semantic tree treat Map/Set as ObjectType, then they will be boxed to extref. Here we avoid this cast if we find the actual object should be fallbacked to libdyntype */ if ( fromObjType.meta && BuiltinNames.fallbackConstructors.includes(fromObjType.meta.name) ) { const fromValueRef = this.wasmExprGen(fromValue); return fromValueRef; } let castedValueRef: binaryen.ExpressionRef; if (fromValue instanceof NewLiteralArrayValue) { const arrLen = fromValue.initValues.length; const currentFuncCtx = this.wasmCompiler.currentFuncCtx!; const arrLenVar = currentFuncCtx.i32Local(); const initArrLenStmt = this.module.local.set( arrLenVar.index, this.module.i32.const(0), ); currentFuncCtx.insert(initArrLenStmt); // compute the true length of new array for (let i = 0; i < arrLen; ++i) { const initValue = fromValue.initValues[i]; if (initValue instanceof SpreadValue) { const propLenRef = this.getStringOffset('length'); const arrLenRef = FunctionalFuncs.getArrayRefLen( this.module, this.wasmExprGen(initValue.target), initValue.target, propLenRef, true, ); const incStmt = this.module.local.set( arrLenVar.index, this.module.i32.add( this.module.local.get( arrLenVar.index, arrLenVar.type, ), arrLenRef, ), ); currentFuncCtx.insert(incStmt); } else { const incStmt = this.module.local.set( arrLenVar.index, this.module.i32.add( this.module.local.get( arrLenVar.index, arrLenVar.type, ), this.module.i32.const(1), ), ); currentFuncCtx.insert(incStmt); } } castedValueRef = FunctionalFuncs.boxLiteralToAny( this.module, fromValue, this.module.local.get(arrLenVar.index, arrLenVar.type), ); } else if (fromValue instanceof NewLiteralObjectValue) { castedValueRef = FunctionalFuncs.boxLiteralToAny( this.module, fromValue, ); } else { const fromValueRef = this.wasmExprGen(fromValue); castedValueRef = FunctionalFuncs.boxToAny( this.module, fromValueRef, fromValue, ); } if ( fromValue instanceof NewLiteralObjectValue || fromValue instanceof NewLiteralArrayValue ) { /* created a temVar to store dynObjValue, then set dyn property */ const tmpVar = this.wasmCompiler.currentFuncCtx!.insertTmpVar( this.wasmTypeGen.getWASMType(Primitive.Any), ); const createDynObjOps: binaryen.ExpressionRef[] = []; createDynObjOps.push( this.module.local.set(tmpVar.index, castedValueRef), ); const forLoopIdx = this.wasmCompiler.currentFuncCtx!.i32Local(); const curElemIdx = this.wasmCompiler.currentFuncCtx!.i32Local(); this.wasmCompiler.currentFuncCtx!.insert( this.module.local.set( curElemIdx.index, this.module.i32.const(0), ), ); for (let i = 0; i < fromValue.initValues.length; i++) { const initValue = fromValue.initValues[i]; let initValueRef = this.wasmExprGen(initValue); if (fromValue instanceof NewLiteralObjectValue) { const propName = fromObjType.meta.members[i].name; const propNameRef = this.getStringOffset(propName); createDynObjOps.push( FunctionalFuncs.setDynObjProp( this.module, this.module.local.get(tmpVar.index, tmpVar.type), propNameRef, initValueRef, ), ); } else { if (initValue instanceof SpreadValue) { const spreadValue = initValue; const propLenRef = this.getStringOffset('length'); const arrLenRef = FunctionalFuncs.getArrayRefLen( this.module, this.wasmExprGen(spreadValue.target), spreadValue.target, propLenRef, true, ); const for_label = 'for_loop_block'; const for_init = this.module.local.set( forLoopIdx.index, this.module.i32.const(0), ); const for_condition = this.module.i32.lt_u( this.module.local.get( forLoopIdx.index, forLoopIdx.type, ), arrLenRef, ); const for_incrementor = this.module.local.set( forLoopIdx.index, this.module.i32.add( this.module.local.get( forLoopIdx.index, forLoopIdx.type, ), this.module.i32.const(1), ), ); let getArrElemStmt: binaryen.ExpressionRef | undefined; if ( spreadValue.target.type.kind == ValueTypeKind.ARRAY ) { const arrayOriTypeRef = this.wasmTypeGen.getWASMArrayOriType( spreadValue.target.type, ); const arrRef = initValueRef; getArrElemStmt = binaryenCAPI._BinaryenArrayGet( this.module.ptr, arrRef, this.module.local.get( forLoopIdx.index, forLoopIdx.type, ), arrayOriTypeRef, false, ); // box the element by dyntype_new_xxx const elemType = ( spreadValue.target.type as ArrayType ).element; if (elemType.isPrimitive) { getArrElemStmt = FunctionalFuncs.boxBaseTypeToAny( this.module, getArrElemStmt, elemType.kind, ); } else { getArrElemStmt = FunctionalFuncs.boxToAny( this.module, this.wasmExprGen(spreadValue.target), spreadValue.target, ); } } else if ( spreadValue.target.type.kind == ValueTypeKind.ANY ) { getArrElemStmt = FunctionalFuncs.getDynArrElem( this.module, initValueRef, this.module.local.get( forLoopIdx.index, forLoopIdx.type, ), ); } const for_body = this.module.block(null, [ FunctionalFuncs.setDynArrElem( this.module, this.module.local.get( tmpVar.index, tmpVar.type, ), this.module.local.get( curElemIdx.index, curElemIdx.type, ), getArrElemStmt!, ), this.module.local.set( curElemIdx.index, this.module.i32.add( this.module.local.get( curElemIdx.index, curElemIdx.type, ), this.module.i32.const(1), ), ), ]); const flattenLoop: FlattenLoop = { label: for_label, condition: for_condition, statements: for_body, incrementor: for_incrementor, }; createDynObjOps.push(for_init); createDynObjOps.push( this.module.loop( for_label, FunctionalFuncs.flattenLoopStatement( this.module, flattenLoop, SemanticsKind.FOR, ), ), ); } else { initValueRef = FunctionalFuncs.boxToAny( this.module, initValueRef, initValue, ); createDynObjOps.push( FunctionalFuncs.setDynArrElem( this.module, this.module.local.get( tmpVar.index, tmpVar.type, ), this.module.local.get( curElemIdx.index, curElemIdx.type, ), initValueRef, ), this.module.local.set( curElemIdx.index, this.module.i32.add( this.module.local.get( curElemIdx.index, curElemIdx.type, ), this.module.i32.const(1), ), ), ); } } } createDynObjOps.push( this.module.local.get(tmpVar.index, tmpVar.type), ); castedValueRef = this.module.block( null, createDynObjOps, tmpVar.type, ); } return castedValueRef; } private createTmpVarOfSpecifiedType( expr: binaryen.ExpressionRef, type: ValueType, ) { const ctx = this.wasmCompiler.currentFuncCtx!; const tmpVar = ctx.insertTmpVar(this.wasmTypeGen.getWASMType(type)); ctx.insert(this.module.local.set(tmpVar.index, expr)); return this.module.local.get(tmpVar.index, tmpVar.type); } private wasmReBinding(value: ReBindingValue) { const ctxVar = value.contextVar; const ctxType = ctxVar.type as ClosureContextType; const ctxTypeRef = this.wasmTypeGen.getWASMType(ctxType); const ctxHeapTypeRef = this.wasmTypeGen.getWASMHeapType(ctxType); const fields: binaryen.ExpressionRef[] = []; fields.push( binaryenCAPI._BinaryenStructGet( this.module.ptr, 0, this.module.local.get(ctxVar.index, ctxTypeRef), ctxTypeRef, false, ), ); for (let i = 0; i < ctxType.freeVarTypeList.length; i++) { fields.push( binaryenCAPI._BinaryenStructGet( this.module.ptr, i + 1, this.module.local.get(ctxVar.index, ctxTypeRef), ctxTypeRef, false, ), ); } const newCtxStruct = binaryenCAPI._BinaryenStructNew( this.module.ptr, arrayToPtr(fields).ptr, fields.length, ctxHeapTypeRef, ); return this.module.local.set(ctxVar.index, newCtxStruct); } private wasmSpread(value: SpreadValue) { const target = value.target; if (target.type.kind == ValueTypeKind.ARRAY) { const arrayStructRef = this.wasmExprGen(target); const arrayStructHeapType = this.wasmTypeGen.getWASMHeapType( target.type, ); const arrayRef = binaryenCAPI._BinaryenStructGet( this.module.ptr, 0, arrayStructRef, arrayStructHeapType, false, ); return arrayRef; } else if (target.type.kind == ValueTypeKind.ANY) { return this.wasmExprGen(target); } throw Error('not implemented'); } private wasmElemsToArr(values: SemanticsValue[], arrType: ValueType) { const arrayLen = values.length; let elemRefs: binaryen.ExpressionRef[] = []; const srcArrRefs: binaryen.ExpressionRef[] = []; const arrayOriHeapType = arrType instanceof ArrayType ? this.wasmTypeGen.getWASMArrayOriHeapType(arrType) : this.wasmTypeGen.getWASMHeapType(arrType); const arrayOriTypeRef = arrType instanceof ArrayType ? this.wasmTypeGen.getWASMArrayOriType(arrType) : this.wasmTypeGen.getWASMType(arrType); const arrayStructHeapType = this.wasmTypeGen.getWASMHeapType(arrType); const elemType = arrType instanceof ArrayType ? arrType.element : (arrType as WASMArrayType).arrayType.element; const statementArray: binaryenCAPI.ExpressionRef[] = []; let needCopy = false; for (let i = 0; i < arrayLen; i++) { let elemValue = values[i]; if ( elemType.kind != ValueTypeKind.ANY && (elemValue.kind == SemanticsValueKind.VALUE_CAST_ANY || elemValue.kind == SemanticsValueKind.OBJECT_CAST_ANY) ) { elemValue = (elemValue as CastValue).value; } const elemRef = this.wasmExprGen(elemValue); if (elemValue.kind == SemanticsValueKind.SPREAD) { needCopy = true; if (elemRefs.length != 0) { const elemArrRef = binaryenCAPI._BinaryenArrayNewFixed( this.module.ptr, arrayOriHeapType, arrayToPtr(elemRefs).ptr, elemRefs.length, ); const elemArrLocal = this.wasmCompiler.currentFuncCtx!.insertTmpVar( binaryen.getExpressionType(elemArrRef), ); const setElemArrLocalStmt = this.module.local.set( elemArrLocal.index, elemArrRef, ); const getElemArrLocalStmt = this.module.local.get( elemArrLocal.index, elemArrLocal.type, ); statementArray.push(setElemArrLocalStmt); srcArrRefs.push(getElemArrLocalStmt); elemRefs = []; } const target = (elemValue as SpreadValue).target; if (target.type.kind == ValueTypeKind.ARRAY) { // box to interface const targetElemType = (target.type as ArrayType).element; if ( elemType instanceof ObjectType && elemType.meta.isInterface && targetElemType instanceof ObjectType && (targetElemType.meta.type == ObjectDescriptionType.OBJECT_INSTANCE || targetElemType.meta.type == ObjectDescriptionType.OBJECT_CLASS || targetElemType.meta.type == ObjectDescriptionType.OBJECT_LITERAL) ) { const arrLenRef = binaryenCAPI._BinaryenArrayLen( this.module.ptr, elemRef, ); const newArrRef = binaryenCAPI._BinaryenArrayNew( this.module.ptr, arrayOriHeapType, arrLenRef, binaryen.none, ); const newArrLocal = this.wasmCompiler.currentFuncCtx!.insertTmpVar( binaryen.getExpressionType(newArrRef), ); const setNewArrLocalStmt = this.module.local.set( newArrLocal.index, newArrRef, ); statementArray.push(setNewArrLocalStmt); const getNewArrLocalStmt = this.module.local.get( newArrLocal.index, newArrLocal.type, ); // create a loop to box every element to interface const forLoopIdx = this.wasmCompiler.currentFuncCtx!.i32Local(); const for_label = 'for_loop'; const for_init = this.module.local.set( forLoopIdx.index, this.module.i32.const(0), ); const for_condition = this.module.i32.lt_u( this.module.local.get( forLoopIdx.index, forLoopIdx.type, ), arrLenRef, ); const for_incrementor = this.module.local.set( forLoopIdx.index, this.module.i32.add( this.module.local.get( forLoopIdx.index, forLoopIdx.type, ), this.module.i32.const(1), ), ); const for_body = binaryenCAPI._BinaryenArraySet( this.module.ptr, getNewArrLocalStmt, this.module.local.get( forLoopIdx.index, forLoopIdx.type, ), binaryenCAPI._BinaryenArrayGet( this.module.ptr, elemRef, this.module.local.get( forLoopIdx.index, forLoopIdx.type, ), arrayOriTypeRef, false, ), ); const flattenLoop: FlattenLoop = { label: for_label, condition: for_condition, statements: for_body, incrementor: for_incrementor, }; statementArray.push(for_init); statementArray.push( this.module.loop( for_label, FunctionalFuncs.flattenLoopStatement( this.module, flattenLoop, SemanticsKind.FOR, ), ), ); srcArrRefs.push(getNewArrLocalStmt); } else { const arrRef = elemRef; srcArrRefs.push(arrRef); } } else if (target.type.kind == ValueTypeKind.ANY) { const anyArrRef = elemRef; const propNameRef = this.getStringOffset('length'); // get the length of any array const arrLenLocal = this.wasmCompiler.currentFuncCtx!.i32Local(); const setArrLenStmt = this.module.local.set( arrLenLocal.index, this.module.i32.trunc_u.f64( FunctionalFuncs.unboxAnyToBase( this.module, FunctionalFuncs.getDynObjProp( this.module, anyArrRef, propNameRef, ), ValueTypeKind.NUMBER, ), ), ); statementArray.push(setArrLenStmt); // create a new array const newArr = binaryenCAPI._BinaryenArrayNew( this.module.ptr, arrayOriHeapType, this.module.local.get( arrLenLocal.index, arrLenLocal.type, ), binaryen.none, ); const newArrLocal = this.wasmCompiler.currentFuncCtx!.insertTmpVar( binaryen.getExpressionType(newArr), ); const setNewArrLocalStmt = this.module.local.set( newArrLocal.index, newArr, ); statementArray.push(setNewArrLocalStmt); // create a loop to set the new array const forLoopIdx = this.wasmCompiler.currentFuncCtx!.i32Local(); const for_label = 'for_loop'; const for_init = this.module.local.set( forLoopIdx.index, this.module.i32.const(0), ); const for_condition = this.module.i32.lt_u( this.module.local.get( forLoopIdx.index, forLoopIdx.type, ), this.module.local.get( arrLenLocal.index, arrLenLocal.type, ), ); const for_incrementor = this.module.local.set( forLoopIdx.index, this.module.i32.add( this.module.local.get( forLoopIdx.index, forLoopIdx.type, ), this.module.i32.const(1), ), ); let getDynArrElemStmt: binaryenCAPI.ExpressionRef; if (elemType.isPrimitive) { getDynArrElemStmt = FunctionalFuncs.unboxAnyToBase( this.module, FunctionalFuncs.getDynArrElem( this.module, anyArrRef, this.module.local.get( forLoopIdx.index, forLoopIdx.type, ), ), elemType.kind, ); } else { getDynArrElemStmt = FunctionalFuncs.getDynArrElem( this.module, anyArrRef, this.module.local.get( forLoopIdx.index, forLoopIdx.type, ), ); } const for_body = binaryenCAPI._BinaryenArraySet( this.module.ptr, this.module.local.get( newArrLocal.index, newArrLocal.type, ), this.module.local.get( forLoopIdx.index, forLoopIdx.type, ), getDynArrElemStmt, ); const flattenLoop: FlattenLoop = { label: for_label, condition: for_condition, statements: for_body, incrementor: for_incrementor, }; statementArray.push(for_init); statementArray.push( this.module.loop( for_label, FunctionalFuncs.flattenLoopStatement( this.module, flattenLoop, SemanticsKind.FOR, ), ), ); srcArrRefs.push( this.module.local.get( newArrLocal.index, newArrLocal.type, ), ); } else { throw Error('not implemented'); } } else { elemRefs.push(elemRef); } } const elemArrRef = binaryenCAPI._BinaryenArrayNewFixed( this.module.ptr, arrayOriHeapType, arrayToPtr(elemRefs).ptr, elemRefs.length, ); const elemArrLocal = this.wasmCompiler.currentFuncCtx!.insertTmpVar(arrayOriTypeRef); const setElemArrLocalStmt = this.module.local.set( elemArrLocal.index, elemArrRef, ); const getElemArrLocalStmt = this.module.local.get( elemArrLocal.index, elemArrLocal.type, ); statementArray.push(setElemArrLocalStmt); srcArrRefs.push(getElemArrLocalStmt); elemRefs = []; let finalArrRef: binaryen.ExpressionRef; let finalArrLenRef: binaryen.ExpressionRef; if (needCopy) { const resConcatArr = this.wasmArrayConcat( srcArrRefs, arrayOriHeapType, statementArray, ); const newArrLenRef = binaryenCAPI._BinaryenArrayLen( this.module.ptr, this.module.local.get( resConcatArr.local.index, resConcatArr.local.type, ), ); finalArrRef = resConcatArr.ref; finalArrLenRef = newArrLenRef; } else { statementArray.push(getElemArrLocalStmt); finalArrRef = this.module.block(null, statementArray); finalArrLenRef = this.module.i32.const(arrayLen); } if (arrType instanceof ArrayType) { const arrayStructRef = binaryenCAPI._BinaryenStructNew( this.module.ptr, arrayToPtr([finalArrRef, finalArrLenRef]).ptr, 2, arrayStructHeapType, ); const newArrStructLocal = this.wasmCompiler.currentFuncCtx!.insertTmpVar( binaryen.getExpressionType(arrayStructRef), ); const setNewArrStructLocal = this.module.local.set( newArrStructLocal.index, arrayStructRef, ); const getNewArrStructLocal = this.module.local.get( newArrStructLocal.index, newArrStructLocal.type, ); this.wasmCompiler.currentFuncCtx!.insert(setNewArrStructLocal); return getNewArrStructLocal; } else { return finalArrRef; } } private wasmArrayConcat( srcArrRefs: binaryenCAPI.ExpressionRef[], arrTypeRef: binaryenCAPI.ExpressionRef, statementArray: binaryen.ExpressionRef[], ) { // 1. compute the total length of new array // 1.1 create a tmp stores the length const totoal_length = this.wasmCompiler.currentFuncCtx!.i32Local(); const initTotalLenStmt = this.module.local.set( totoal_length.index, this.module.i32.const(0), ); statementArray.push(initTotalLenStmt); const totoal_length_ref = this.module.local.get( totoal_length.index, totoal_length.type, ); // 1.2 caculate the total length for (let i = 0; i < srcArrRefs.length; ++i) { const arrLenRef = binaryenCAPI._BinaryenArrayLen( this.module.ptr, srcArrRefs[i], ); const stmt = this.module.local.set( totoal_length.index, this.module.i32.add(totoal_length_ref, arrLenRef), ); statementArray.push(stmt); } // 2. create a new array // 2.1 create a local variable to store the new array const newArr = binaryenCAPI._BinaryenArrayNew( this.module.ptr, arrTypeRef, totoal_length_ref, binaryen.none, ); const newArrLocal = this.wasmCompiler.currentFuncCtx!.insertTmpVar( binaryen.getExpressionType(newArr), ); // 2.2 set the new array const initArrStmt = this.module.local.set(newArrLocal.index, newArr); statementArray.push(initArrStmt); const newArrRef = this.module.local.get( newArrLocal.index, newArrLocal.type, ); //3. create a local variable to store the num of copied elems const copiedNum = this.wasmCompiler.currentFuncCtx!.i32Local(); const initCopiedNumStmt = this.module.local.set( copiedNum.index, this.module.i32.const(0), ); statementArray.push(initCopiedNumStmt); const copiedNumRef = this.module.local.get( copiedNum.index, copiedNum.type, ); //4. copy all of the elements to the new array for (let i = 0; i < srcArrRefs.length; ++i) { const srcArrLenRef = binaryenCAPI._BinaryenArrayLen( this.module.ptr, srcArrRefs[i], ); const copyStmt = binaryenCAPI._BinaryenArrayCopy( this.module.ptr, newArrRef, copiedNumRef, srcArrRefs[i], this.module.i32.const(0), srcArrLenRef, ); statementArray.push(copyStmt); const incCopiedNumStmt = this.module.local.set( copiedNum.index, this.module.i32.add(copiedNumRef, srcArrLenRef), ); statementArray.push(incCopiedNumStmt); } statementArray.push(newArrRef); return { local: newArrLocal, ref: this.module.block(null, statementArray), }; } } ================================================ FILE: src/backend/binaryen/wasm_stmt_gen.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import binaryen from 'binaryen'; import * as binaryenCAPI from './glue/binaryen.js'; import { FlattenLoop, FunctionalFuncs } from './utils.js'; import { WASMGen } from './index.js'; import { BasicBlockNode, BlockNode, BreakNode, CaseClauseNode, ContinueNode, DefaultClauseNode, ForNode, IfNode, ReturnNode, SemanticsKind, SemanticsNode, SwitchNode, ThrowNode, TryNode, VarDeclareNode, WhileNode, } from '../../semantics/semantics_nodes.js'; import { ClosureContextType, Primitive, ValueType, } from '../../semantics/value_types.js'; import ts from 'typescript'; import { BuiltinNames } from '../../../lib/builtin/builtin_name.js'; import { SemanticsValue, VarValue } from '../../semantics/value.js'; import { getConfig } from '../../../config/config_mgr.js'; export class WASMStatementGen { private module; constructor(private wasmCompiler: WASMGen) { this.module = this.wasmCompiler.module; } WASMStmtGen(stmt: SemanticsNode): binaryen.ExpressionRef { let res: binaryen.ExpressionRef | null = null; this.module = this.wasmCompiler.module; switch (stmt.kind) { case SemanticsKind.IF: { res = this.wasmIf(stmt); break; } case SemanticsKind.BLOCK: { res = this.wasmBlock(stmt); break; } case SemanticsKind.RETURN: { res = this.wasmReturn(stmt); break; } case SemanticsKind.EMPTY: { res = this.wasmEmpty(); break; } case SemanticsKind.WHILE: case SemanticsKind.DOWHILE: { res = this.wasmLoop(stmt); break; } case SemanticsKind.FOR: { res = this.wasmFor(stmt); break; } case SemanticsKind.SWITCH: { res = this.wasmSwitch(stmt); break; } case SemanticsKind.BREAK: { res = this.wasmBreakOrContinue(stmt); break; } case SemanticsKind.CONTINUE: { res = this.wasmBreakOrContinue(stmt); break; } case SemanticsKind.BASIC_BLOCK: { res = this.wasmBasicExpr(stmt); break; } case SemanticsKind.TRY: { res = this.wasmTry(stmt); break; } case SemanticsKind.THROW: { res = this.wasmThrow(stmt); break; } default: throw new Error('unexpected stmt kind ' + stmt.kind); } this.addDebugInfoRef(stmt, res); return res; } wasmIf(stmt: IfNode): binaryen.ExpressionRef { let wasmCond: binaryen.ExpressionRef = this.wasmCompiler.wasmExprComp.wasmExprGen(stmt.condition); wasmCond = FunctionalFuncs.generateCondition( this.module, wasmCond, stmt.condition.type.kind, ); this.addDebugInfoRef(stmt.condition, wasmCond); /* if ture need to enter into a new scope */ this.wasmCompiler.currentFuncCtx!.enterScope(); this.wasmCompiler.currentFuncCtx!.insert( this.WASMStmtGen(stmt.trueNode), ); const ifTrueStmts = this.wasmCompiler.currentFuncCtx!.exitScope(); const wasmIfTrue: binaryen.ExpressionRef = this.module.block( null, ifTrueStmts, ); /* if false need to enter into a new scope */ let wasmIfFalse = undefined; if (stmt.falseNode) { this.wasmCompiler.currentFuncCtx!.enterScope(); this.wasmCompiler.currentFuncCtx!.insert( this.WASMStmtGen(stmt.falseNode), ); const ifFalseStmts = this.wasmCompiler.currentFuncCtx!.exitScope(); wasmIfFalse = this.module.block(null, ifFalseStmts); } return this.module.if(wasmCond, wasmIfTrue, wasmIfFalse); } wasmBlock(stmt: BlockNode): binaryen.ExpressionRef { this.wasmCompiler.currentFuncCtx!.enterScope(); /* assign value for block's context variable */ if ( stmt.varList && stmt.varList[0].type instanceof ClosureContextType && stmt.varList[0].initCtx ) { const freeVars: VarDeclareNode[] = []; for (const v of stmt.varList) { if (v.closureIndex !== undefined) { freeVars.push(v); } } this.wasmCompiler.assignCtxVar(stmt.varList[0], freeVars); } for (const child of stmt.statements) { const childRef = this.WASMStmtGen(child); this.wasmCompiler.currentFuncCtx!.insert(childRef); } const statements = this.wasmCompiler.currentFuncCtx!.exitScope(); return this.module.block(null, statements); } wasmReturn(stmt: ReturnNode): binaryen.ExpressionRef { const brReturn = this.module.br('statements'); if (stmt.expr === undefined) { return brReturn; } const returnExprRef = this.wasmCompiler.wasmExprComp.wasmExprGen( stmt.expr, ); this.addDebugInfoRef(stmt.expr, returnExprRef); if (binaryen.getExpressionType(returnExprRef) !== binaryen.none) { const setReturnValue = this.module.local.set( this.wasmCompiler.currentFuncCtx!.returnIdx, returnExprRef, ); this.addDebugInfoRef(stmt, setReturnValue); this.wasmCompiler.currentFuncCtx!.insert(setReturnValue); } else { this.wasmCompiler.currentFuncCtx!.insert(returnExprRef); } return brReturn; } wasmEmpty(): binaryen.ExpressionRef { return this.wasmCompiler.module.nop(); } wasmLoop(stmt: WhileNode): binaryen.ExpressionRef { this.wasmCompiler.currentFuncCtx!.enterScope(); let WASMCond: binaryen.ExpressionRef = this.wasmCompiler.wasmExprComp.wasmExprGen(stmt.condition); const WASMStmts: binaryen.ExpressionRef = this.WASMStmtGen(stmt.body!); WASMCond = FunctionalFuncs.generateCondition( this.module, WASMCond, stmt.condition.type.kind, ); this.addDebugInfoRef(stmt.condition, WASMCond); // this.WASMCompiler.addDebugInfoRef(stmt.loopCondtion, WASMCond); // (block $break // (loop $loop_label // ... // (if cond // ... // (br $loop_label) // ) // ) // ) const flattenLoop: FlattenLoop = { label: stmt.label, continueLabel: stmt.continueLabel ?? undefined, condition: WASMCond, statements: WASMStmts, }; this.wasmCompiler.currentFuncCtx!.insert( this.module.loop( stmt.label, FunctionalFuncs.flattenLoopStatement( this.module, flattenLoop, stmt.kind, ), ), ); const statements = this.wasmCompiler.currentFuncCtx!.exitScope(); return this.module.block(stmt.blockLabel, statements); } wasmFor(stmt: ForNode): binaryen.ExpressionRef { this.wasmCompiler.currentFuncCtx!.enterScope(); let WASMCond: binaryen.ExpressionRef | undefined; let WASMIncrementor: binaryen.ExpressionRef | undefined; let WASMStmts: binaryen.ExpressionRef = this.wasmCompiler.module.nop(); if (stmt.initialize) { const init = this.WASMStmtGen(stmt.initialize); if (stmt.initialize.kind === SemanticsKind.BASIC_BLOCK) { this.wasmCompiler.currentFuncCtx!.insert(init); } } if (stmt.condition) { WASMCond = this.wasmCompiler.wasmExprComp.wasmExprGen( stmt.condition, ); this.addDebugInfoRef(stmt.condition, WASMCond); } if (stmt.next) { WASMIncrementor = this.wasmCompiler.wasmExprComp.wasmExprGen( stmt.next, ); this.addDebugInfoRef(stmt.next, WASMIncrementor); } if (stmt.body) { WASMStmts = this.WASMStmtGen(stmt.body); } const flattenLoop: FlattenLoop = { label: stmt.label, continueLabel: stmt.continueLabel ?? undefined, condition: WASMCond, statements: WASMStmts, incrementor: WASMIncrementor, }; this.wasmCompiler.currentFuncCtx!.insert( this.module.loop( stmt.label, FunctionalFuncs.flattenLoopStatement( this.module, flattenLoop, stmt.kind, ), ), ); const statements = this.wasmCompiler.currentFuncCtx!.exitScope(); return this.module.block(stmt.blockLabel, statements); } wasmSwitch(stmt: SwitchNode): binaryen.ExpressionRef { const caseClause = stmt.caseClause; const defaultClause = stmt.defaultClause; const defaultClauseLen = defaultClause ? 1 : 0; const indexOfDefault = defaultClause ? caseClause.length : -1; /* set branches labels */ const branches: binaryen.ExpressionRef[] = new Array( caseClause.length + defaultClauseLen, ); caseClause.forEach((clause, i) => { branches[i] = this.module.br( 'case' + i + stmt.label, this.wasmCompiler.wasmExprComp.operateBinaryExpr( stmt.condition, clause.caseVar, ts.SyntaxKind.EqualsEqualsEqualsToken, ), ); }); if (defaultClause) { branches[indexOfDefault] = this.module.br( 'case' + indexOfDefault + stmt.label, ); } /* set blocks */ let block = this.module.block('case0' + stmt.label, branches); caseClause.forEach((clause, i) => { let label: string; if (i === caseClause.length - 1 && !defaultClause) { label = stmt.breakLabel; } else { label = 'case' + (i + 1) + stmt.label; } block = this.module.block( label, [block].concat(this.WASMStmtGen(clause.body!)), ); }); if (defaultClause) { block = this.module.block( stmt.breakLabel, [block].concat(this.WASMStmtGen(defaultClause.body!)), ); } return block; } wasmBreakOrContinue( stmt: BreakNode | ContinueNode, ): binaryen.ExpressionRef { return this.wasmCompiler.module.br(stmt.label); } wasmBasicExpr(stmt: BasicBlockNode): binaryen.ExpressionRef { this.wasmCompiler.currentFuncCtx!.enterScope(); for (const exprStmt of stmt.valueNodes) { const exprRef = this.wasmCompiler.wasmExprComp.wasmExprGen(exprStmt); this.addDebugInfoRef(exprStmt, exprRef); this.wasmCompiler.currentFuncCtx!.insert(exprRef); } const basicStmts = this.wasmCompiler.currentFuncCtx!.exitScope(); return this.module.block(null, basicStmts); } wasmTry(stmt: TryNode): binaryen.ExpressionRef { /* set tmp var */ const tmpNeedRethrow = this.wasmCompiler.currentFuncCtx!.insertTmpVar( this.wasmCompiler.wasmTypeComp.getWASMType(Primitive.Boolean), ); const tmpException = this.wasmCompiler.currentFuncCtx!.insertTmpVar( this.wasmCompiler.wasmTypeComp.getWASMType(Primitive.Any), ); const getTmpExceptionRef = this.module.local.get( tmpException.index, tmpException.type, ); const setTmpExceptionRef = this.module.local.set( tmpException.index, this.module.anyref.pop(), ); /* generate structure for one layer of ts try statement */ const tryTSLable = stmt.label; const originTryRef = this.WASMStmtGen(stmt.body); const tryInCatchRef = this.module.block(null, [ this.module.local.set( tmpNeedRethrow.index, this.module.i32.const(1), ), originTryRef, ]); const tryCatchLabel = tryTSLable.concat('_catch'); const catchRefs: binaryen.ExpressionRef[] = [setTmpExceptionRef]; if (stmt.catchClause) { /* set tmpException value to e variable: catch (e) */ if (stmt.catchClause.catchVar) { const catchVarValue = stmt.catchClause.catchVar as VarValue; const catchVarDeclNode = catchVarValue.ref as VarDeclareNode; catchRefs.push( this.module.local.set( catchVarDeclNode.index, getTmpExceptionRef, ), ); } const originCatchRef = this.WASMStmtGen(stmt.catchClause.body); catchRefs.push( this.module.local.set( tmpNeedRethrow.index, this.module.i32.const(0), ), ); catchRefs.push(originCatchRef); } catchRefs.push( this.module.throw(BuiltinNames.finallyTag, [getTmpExceptionRef]), ); const innerTryRef = this.module.try( tryCatchLabel, tryInCatchRef, [BuiltinNames.errorTag], [this.module.block(null, catchRefs)], ); const tryFinallyLabel = tryTSLable.concat('_finally'); const finallyRefs: binaryen.ExpressionRef[] = [setTmpExceptionRef]; if (stmt.finallyBlock) { const originFinallyRef = this.WASMStmtGen(stmt.finallyBlock); finallyRefs.push(originFinallyRef); } finallyRefs.push( this.module.if( this.module.i32.eq( this.module.local.get( tmpNeedRethrow.index, tmpNeedRethrow.type, ), this.module.i32.const(1), ), this.module.throw(BuiltinNames.errorTag, [getTmpExceptionRef]), ), ); const outerTryRef = this.module.try( tryFinallyLabel, innerTryRef, [BuiltinNames.finallyTag], [this.module.block(null, finallyRefs)], ); return outerTryRef; } wasmThrow(stmt: ThrowNode): binaryen.ExpressionRef { const throwExpr = stmt.throwExpr; const exprRef = this.wasmCompiler.wasmExprComp.wasmExprGen(throwExpr); this.addDebugInfoRef(throwExpr, exprRef); /* workaround: only support anyref error in the first version */ return this.module.throw(BuiltinNames.errorTag, [ FunctionalFuncs.boxToAny(this.module, exprRef, throwExpr), ]); } addDebugInfoRef( irNode: SemanticsNode | SemanticsValue, ref: binaryen.ExpressionRef, ) { if (getConfig().sourceMap && irNode.location) { this.wasmCompiler.currentFuncCtx!.sourceMapLocs.push({ location: irNode.location, ref: ref, }); } } } ================================================ FILE: src/backend/binaryen/wasm_type_gen.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import binaryen from 'binaryen'; import * as binaryenCAPI from './glue/binaryen.js'; import { arrayToPtr, emptyStructType, initArrayType, initStructType, createSignatureTypeRefAndHeapTypeRef, Packed, generateArrayStructTypeInfo, builtinClosureType, generateArrayStructTypeForRec, ptrToArray, baseVtableType, baseStructType, } from './glue/transform.js'; import { assert } from 'console'; import { arrayBufferTypeInfo, dataViewTypeInfo, infcTypeInfo, stringTypeInfo, } from './glue/packType.js'; import { WASMGen } from './index.js'; import { ArrayType, ClosureContextType, EmptyType, EnumType, FunctionType, ObjectType, Primitive, TupleType, TypeParameterType, UnionType, ValueType, ValueTypeKind, WASMArrayType, WASMStructType, } from '../../semantics/value_types.js'; import { UnimplementError } from '../../error.js'; import { MemberModifier, MemberType, ObjectDescription, } from '../../semantics/runtime.js'; import { FunctionalFuncs, UtilFuncs } from './utils.js'; import { BuiltinNames } from '../../../lib/builtin/builtin_name.js'; import { VarValue } from '../../semantics/value.js'; import { needSpecialized } from '../../semantics/type_creator.js'; import { getConfig } from '../../../config/config_mgr.js'; import { MutabilityKind, NullabilityKind, PackedTypeKind, } from '../../utils.js'; import { typeInfo } from './glue/utils.js'; export class WASMTypeGen { private typeMap: Map = new Map(); /** it used for rec types, they share this._tb */ private _tb: binaryenCAPI.TypeBuilderRef = binaryenCAPI._TypeBuilderCreate(1); private heapTypeMap: Map = new Map(); /* For array, we store array's struct type in type map, store array type in oriArrayTypeMap */ private oriArrayTypeMap: Map = new Map(); private oriArrayHeapTypeMap: Map = new Map(); /* closure format is : {context: struct{}, funcref: ref $func} */ private closureStructTypeMap: Map = new Map(); private closureStructHeapTypeMap: Map = new Map(); private funcParamTypesMap: Map = new Map(); private funcOriParamTypesMap: Map = new Map(); private vtableTypeMap: Map = new Map(); private vtableHeapTypeMap: Map = new Map(); private vtableInstMap: Map = new Map(); private thisInstMap: Map = new Map(); private staticFieldsTypeMap: Map = new Map(); private staticFieldsHeapTypeMap: Map = new Map(); private staticFieldsUpdateMap: Map = new Map(); private infcObjTypeMap: Map = new Map(); private infcObjHeapTypeMap: Map = new Map(); public objTypeMap: Map = new Map(); private structHeapTypeCnt = 0; private arrayHeapTypeCnt = 0; private arrayTypeCnt = 0; private funcHeapTypeCnt = 0; private contextHeapTypeCnt = 0; private typeToBuilderIdxMap = new Map(); /** records Type to TypeBuilder index auxiliary */ private auxTypeIndexMap = new Map(); /** the entry of rec group circle */ private recStartElem: ObjectType | null = null; constructor(private wasmComp: WASMGen) { // } parseCircularRecType(): void { /** parsing recursive types firstly */ const recTypes = this.wasmComp.semanticModule.recObjectTypeGroup; for (let i = 0; i < recTypes.length; i++) { for (let j = 0; j < recTypes[i].length; j++) { this.typeToBuilderIdxMap.set(recTypes[i][j], j); } if (recTypes[i].length > 1) { binaryenCAPI._TypeBuilderGrow(this._tb, recTypes[i].length - 1); } this.recStartElem = recTypes[i][0]; this.createWASMObjectType(this.recStartElem); this.typeToBuilderIdxMap.clear(); this.auxTypeIndexMap.clear(); this.recStartElem = null; this._tb = binaryenCAPI._TypeBuilderCreate(1); } } createWASMType(type: ValueType): void { switch (type.kind) { case ValueTypeKind.VOID: case ValueTypeKind.BOOLEAN: case ValueTypeKind.NUMBER: case ValueTypeKind.STRING: case ValueTypeKind.RAW_STRING: case ValueTypeKind.UNDEFINED: case ValueTypeKind.UNION: case ValueTypeKind.ANY: case ValueTypeKind.INT: case ValueTypeKind.WASM_I64: case ValueTypeKind.WASM_F32: this.createWASMBaseType(type); break; case ValueTypeKind.NULL: case ValueTypeKind.EMPTY: this.createWASMEmptyType(type); break; case ValueTypeKind.CLOSURECONTEXT: this.createWASMContextType(type); break; case ValueTypeKind.TYPE_PARAMETER: this.createWASMSpecializeType(type); break; case ValueTypeKind.GENERIC: this.createWASMGenericType(type); break; case ValueTypeKind.ARRAY: this.createWASMArrayType(type); break; case ValueTypeKind.FUNCTION: this.createWASMFuncType(type); break; case ValueTypeKind.OBJECT: this.createWASMObjectType(type); break; case ValueTypeKind.ENUM: this.createWASMEnumType(type); break; case ValueTypeKind.TUPLE: this.createWASMTupleType(type); break; case ValueTypeKind.WASM_ARRAY: case ValueTypeKind.WASM_STRUCT: this.createWASMRawType(type); break; default: throw new UnimplementError(`createWASMType: ${type}`); } } createWASMBaseType(type: ValueType): void { if (this.typeMap.has(type)) { return; } switch (type.kind) { case ValueTypeKind.VOID: this.typeMap.set(type, binaryen.none); break; case ValueTypeKind.BOOLEAN: this.typeMap.set(type, binaryen.i32); break; case ValueTypeKind.NUMBER: this.typeMap.set(type, binaryen.f64); break; case ValueTypeKind.INT: this.typeMap.set(type, binaryen.i32); break; case ValueTypeKind.RAW_STRING: case ValueTypeKind.STRING: { const stringType = getConfig().enableStringRef ? binaryenCAPI._BinaryenTypeStringref() : stringTypeInfo.typeRef; const stringHeapType = getConfig().enableStringRef ? binaryenCAPI._BinaryenHeapTypeString() : stringTypeInfo.heapTypeRef; this.typeMap.set(type, stringType); this.heapTypeMap.set(type, stringHeapType); this.createCustomTypeName( 'string_type', stringTypeInfo.heapTypeRef, ); break; } case ValueTypeKind.UNDEFINED: case ValueTypeKind.ANY: case ValueTypeKind.UNION: this.typeMap.set(type, binaryen.anyref); break; case ValueTypeKind.WASM_I64: this.typeMap.set(type, binaryen.i64); break; case ValueTypeKind.WASM_F32: this.typeMap.set(type, binaryen.f32); break; default: break; } } createWASMEmptyType(type: EmptyType) { this.typeMap.set(type, emptyStructType.typeRef); this.heapTypeMap.set(type, emptyStructType.heapTypeRef); } createWASMContextType(type: ClosureContextType) { let typeRef: binaryenCAPI.TypeRef; let heapTypeRef: binaryenCAPI.HeapTypeRef; const parentTypeRef = type.parentCtxType ? this.getWASMValueType(type.parentCtxType) : emptyStructType.typeRef; const parentHeapTypeRef = type.parentCtxType ? this.getWASMValueHeapType(type.parentCtxType) : emptyStructType.heapTypeRef; if (type.freeVarTypeList.length > 0) { const contextStructLength = type.freeVarTypeList.length + 1; const contextStructTypeRefArray: binaryenCAPI.TypeRef[] = new Array( contextStructLength, ); contextStructTypeRefArray[0] = parentTypeRef; for (let i = 0; i < type.freeVarTypeList.length; i++) { const freeVarTypeRef = this.getWASMValueType( type.freeVarTypeList[i], ); contextStructTypeRefArray[i + 1] = freeVarTypeRef; } const fieldPackedTypesList: binaryenCAPI.PackedType[] = new Array( contextStructLength, ).fill(Packed.Not); const fieldMutablesList: boolean[] = new Array( contextStructLength, ).fill(true); const contextStructTypeInfo = initStructType( contextStructTypeRefArray, fieldPackedTypesList, fieldMutablesList, contextStructLength, true, -1, binaryenCAPI._TypeBuilderCreate(1), ); typeRef = contextStructTypeInfo.typeRef; heapTypeRef = contextStructTypeInfo.heapTypeRef; this.createCustomTypeName( `context${this.contextHeapTypeCnt++}`, heapTypeRef, ); } else { typeRef = parentTypeRef; heapTypeRef = parentHeapTypeRef; } this.typeMap.set(type, typeRef); this.heapTypeMap.set(type, heapTypeRef); } createWASMFuncType(funcType: FunctionType) { const resultWASMType = this.getWASMValueType(funcType.returnType); const paramTypes = funcType.argumentsType; const paramWASMTypes = new Array(); const oriParamWASMTypes = new Array(); /* add env params */ for (let i = 0; i < funcType.envParamLen; ++i) { paramWASMTypes.push(emptyStructType.typeRef); } for (let i = 0; i < paramTypes.length; ++i) { const paramTypeRef = this.getWASMValueType(paramTypes[i]); paramWASMTypes.push(paramTypeRef); oriParamWASMTypes.push(paramTypeRef); } /* record original param wasm type */ this.funcParamTypesMap.set(funcType, paramWASMTypes); this.funcOriParamTypesMap.set(funcType, oriParamWASMTypes); let tb = binaryenCAPI._TypeBuilderCreate(1); const buildIndex = this.createTbIndexForType(funcType); if (buildIndex !== -1) { tb = this._tb; const heapType = binaryenCAPI._TypeBuilderGetTempHeapType( tb, buildIndex, ); const refType = binaryenCAPI._TypeBuilderGetTempRefType( tb, heapType, true, ); this.typeMap.set(funcType, refType); this.heapTypeMap.set(funcType, heapType); } const signature = createSignatureTypeRefAndHeapTypeRef( paramWASMTypes, resultWASMType, buildIndex, tb, ); /* create closure type */ let closureTypeIdx = -1; tb = binaryenCAPI._TypeBuilderCreate(1); if (buildIndex !== -1) { tb = this._tb; closureTypeIdx = binaryenCAPI._TypeBuilderGetSize(this._tb); binaryenCAPI._TypeBuilderGrow(this._tb, 1); this.auxTypeIndexMap.set(funcType, closureTypeIdx); const heapType = binaryenCAPI._TypeBuilderGetTempHeapType( tb, closureTypeIdx, ); const refType = binaryenCAPI._TypeBuilderGetTempRefType( tb, heapType, true, ); this.closureStructTypeMap.set(funcType, refType); this.closureStructHeapTypeMap.set(funcType, heapType); } const closureStructType = initStructType( [ emptyStructType.typeRef, emptyStructType.typeRef, signature.typeRef, ], [Packed.Not, Packed.Not, Packed.Not], [true, true, false], 3, true, closureTypeIdx, tb, builtinClosureType.heapTypeRef, ); if (buildIndex === -1) { this.typeMap.set(funcType, signature.typeRef); this.heapTypeMap.set(funcType, signature.heapTypeRef); this.closureStructTypeMap.set(funcType, closureStructType.typeRef); this.closureStructHeapTypeMap.set( funcType, closureStructType.heapTypeRef, ); this.createCustomTypeName( `function${this.funcHeapTypeCnt}`, signature.heapTypeRef, ); this.createCustomTypeName( `closure${this.funcHeapTypeCnt++}`, closureStructType.heapTypeRef, ); } } createWASMArrayType(arrayType: ArrayType) { /** because array type maybe need to specialized, so the same arrayType may be parsed more than once, and binaryen will generate a new * wasm type which doesn't list in rec. */ if (this.getExistWasmArrType(arrayType)) { return; } let tb = binaryenCAPI._TypeBuilderCreate(1); const buildIndex = this.createTbIndexForType(arrayType); if (buildIndex !== -1) { tb = this._tb; const heapType = binaryenCAPI._TypeBuilderGetTempHeapType( tb, buildIndex, ); const refType = binaryenCAPI._TypeBuilderGetTempRefType( tb, heapType, true, ); this.oriArrayTypeMap.set(arrayType, refType); this.oriArrayHeapTypeMap.set(arrayType, heapType); } const elemTypeRef = this.getWASMValueType(arrayType.element); const arrayTypeInfo = initArrayType( elemTypeRef, Packed.Not, true, true, buildIndex, tb, ); let arrayStructTypeInfo; if (buildIndex !== -1) { const idx = binaryenCAPI._TypeBuilderGetSize(this._tb); binaryenCAPI._TypeBuilderGrow(this._tb, 1); this.auxTypeIndexMap.set(arrayType, idx); arrayStructTypeInfo = generateArrayStructTypeForRec( arrayTypeInfo, idx, this._tb, ); } else { arrayStructTypeInfo = generateArrayStructTypeInfo(arrayTypeInfo); this.oriArrayTypeMap.set(arrayType, arrayTypeInfo.typeRef); this.oriArrayHeapTypeMap.set(arrayType, arrayTypeInfo.heapTypeRef); this.createCustomTypeName( `array-struct${this.arrayHeapTypeCnt++}`, arrayStructTypeInfo.heapTypeRef, ); this.createCustomTypeName( `array${this.arrayTypeCnt++}`, arrayTypeInfo.heapTypeRef, ); } this.typeMap.set(arrayType, arrayStructTypeInfo.typeRef); this.heapTypeMap.set(arrayType, arrayStructTypeInfo.heapTypeRef); } createWASMArrayBufferType(type: ObjectType) { this.typeMap.set(type, arrayBufferTypeInfo.typeRef); this.heapTypeMap.set(type, arrayBufferTypeInfo.heapTypeRef); } createWASMDataViewType(type: ObjectType) { this.typeMap.set(type, dataViewTypeInfo.typeRef); this.heapTypeMap.set(type, dataViewTypeInfo.heapTypeRef); } createWASMBuiltinType(type: ObjectType) { const builtinTypeName = type.meta.name; switch (builtinTypeName) { case BuiltinNames.ARRAYBUFFER: { this.createWASMArrayBufferType(type); break; } case BuiltinNames.DATAVIEW: { this.createWASMDataViewType(type); break; } default: { throw new UnimplementError( `${builtinTypeName} builtin type is not supported`, ); } } } createWASMObjectType(type: ObjectType) { const metaInfo = type.meta; if (BuiltinNames.builtInObjectTypes.includes(metaInfo.name)) { this.createWASMBuiltinType(type); } else { if (metaInfo.isInterface) { this.createWASMInfcType(type); this.createWASMClassType(type, true); } else { if (type.meta.isObjectClass) { this.createStaticFields(type); } else { this.createWASMClassType(type); } if ( this.staticFieldsUpdateMap.has(type) && !this.staticFieldsUpdateMap.get(type) ) { this.updateStaticFields(type); } } } } createWASMInfcType(type: ObjectType) { this.typeMap.set(type, infcTypeInfo.typeRef); this.heapTypeMap.set(type, infcTypeInfo.heapTypeRef); } createWASMEnumType(type: EnumType) { this.typeMap.set(type, this.getWASMValueType(type.memberType)); } createWASMTupleType(type: TupleType) { const fieldTypesListRef = new Array(); for (const elementType of type.elements) { fieldTypesListRef.push(this.getWASMValueType(elementType)); } const fieldPackedTypesListRef = new Array( fieldTypesListRef.length, ).fill(Packed.Not); const fieldMutablesListRef = new Array( fieldTypesListRef.length, ).fill(true); const tb = binaryenCAPI._TypeBuilderCreate(1); const buildIndex = this.createTbIndexForType(type); const tupleTypeInfo = initStructType( fieldTypesListRef, fieldPackedTypesListRef, fieldMutablesListRef, fieldTypesListRef.length, true, buildIndex, tb, ); this.typeMap.set(type, tupleTypeInfo.typeRef); this.heapTypeMap.set(type, tupleTypeInfo.heapTypeRef); } createWASMRawType(type: ValueType) { if (this.typeMap.has(type)) { return; } switch (type.kind) { case ValueTypeKind.WASM_ARRAY: this.createWASMArrayRawType(type); break; case ValueTypeKind.WASM_STRUCT: this.createWASMStructRawType(type); break; default: break; } } createWASMArrayRawType(type: WASMArrayType) { let arrRawTypeRef: binaryen.Type; let arrRawHeapTypeRef: binaryenCAPI.HeapTypeRef; let arrayRawTypeInfo: typeInfo; if ( type.packedTypeKind === PackedTypeKind.Not_Packed && type.mutability === MutabilityKind.Mutable && type.nullability === NullabilityKind.Nullable ) { arrRawTypeRef = this.getWASMArrayOriType(type.arrayType); arrRawHeapTypeRef = this.getWASMArrayOriHeapType(type.arrayType); arrayRawTypeInfo = { typeRef: arrRawTypeRef, heapTypeRef: arrRawHeapTypeRef, }; } else { const elemTypeRef = this.getWASMValueType(type.arrayType.element); let elementPackedType: binaryenCAPI.PackedType = Packed.Not; switch (type.packedTypeKind) { case PackedTypeKind.I8: { elementPackedType = Packed.I8; break; } case PackedTypeKind.I16: { elementPackedType = Packed.I16; break; } } let elementMutable: binaryenCAPI.bool = true; if (type.mutability === MutabilityKind.Immutable) { elementMutable = false; } let nullable: binaryenCAPI.bool = true; if (type.nullability === NullabilityKind.NonNullable) { nullable = false; } const tb = binaryenCAPI._TypeBuilderCreate(1); const buildIndex = this.createTbIndexForType(type.arrayType); arrayRawTypeInfo = initArrayType( elemTypeRef, elementPackedType, elementMutable, nullable, buildIndex, tb, ); } this.typeMap.set(type, arrayRawTypeInfo.typeRef); this.heapTypeMap.set(type, arrayRawTypeInfo.heapTypeRef); } createWASMStructRawType(type: WASMStructType) { let structRawTypeRef: binaryen.Type; let structRawHeapTypeRef: binaryenCAPI.HeapTypeRef; let structRawTypeInfo: typeInfo; const isEachFieldNotPacked = type.packedTypeKinds.every( (value) => value === PackedTypeKind.Not_Packed, ); const isEachFieldMutable = type.mutabilitys.every( (value) => value === MutabilityKind.Mutable, ); const isNullable = type.nullability === NullabilityKind.Nullable ? true : false; if ( isEachFieldNotPacked && isEachFieldMutable && isNullable && !type.baseType ) { structRawTypeRef = this.getWASMType(type.tupleType); structRawHeapTypeRef = this.getWASMHeapType(type.tupleType); structRawTypeInfo = { typeRef: structRawTypeRef, heapTypeRef: structRawHeapTypeRef, }; } else { const fieldTypesListRef = new Array(); for (const elementType of type.tupleType.elements) { fieldTypesListRef.push(this.getWASMValueType(elementType)); } const fieldPackedTypesListRef = new Array( fieldTypesListRef.length, ); for (const packedType of type.packedTypeKinds) { let fieldPackedType = Packed.Not; switch (packedType) { case PackedTypeKind.I8: { fieldPackedType = Packed.I8; break; } case PackedTypeKind.I16: { fieldPackedType = Packed.I16; break; } } fieldPackedTypesListRef.push(fieldPackedType); } const fieldMutablesListRef = new Array( fieldTypesListRef.length, ); for (const mutability of type.mutabilitys) { let fieldMutability = true; if (mutability === MutabilityKind.Immutable) { fieldMutability = false; } fieldMutablesListRef.push(fieldMutability); } let nullable = true; if (type.nullability === NullabilityKind.NonNullable) { nullable = false; } const baseTypeRef = type.baseType ? this.getWASMType(type.baseType) : undefined; const tb = binaryenCAPI._TypeBuilderCreate(1); const buildIndex = this.createTbIndexForType(type.tupleType); structRawTypeInfo = initStructType( fieldTypesListRef, fieldPackedTypesListRef, fieldMutablesListRef, fieldTypesListRef.length, nullable, buildIndex, tb, baseTypeRef, ); } this.typeMap.set(type, structRawTypeInfo.typeRef); this.heapTypeMap.set(type, structRawTypeInfo.heapTypeRef); } getObjSpecialSuffix(type: ArrayType) { let specialType: ValueType | undefined = undefined; if (type.specialTypeArguments && type.specialTypeArguments.length > 0) { /* ArrayType only has one specialTypeArgument */ specialType = type.specialTypeArguments[0]; } let methodSuffix = ''; if (specialType) { switch (specialType.kind) { case ValueTypeKind.NUMBER: methodSuffix = '_f64'; break; case ValueTypeKind.INT: case ValueTypeKind.BOOLEAN: methodSuffix = '_i32'; break; case ValueTypeKind.WASM_F32: methodSuffix = '_f32'; break; case ValueTypeKind.WASM_I64: methodSuffix = '_i64'; break; default: methodSuffix = '_anyref'; } } else { methodSuffix = '_anyref'; } return methodSuffix; } createWASMClassType(type: ObjectType, isInfc = false) { const metaInfo = type.meta; let tb = binaryenCAPI._TypeBuilderCreate(1); const buildIndex = this.createTbIndexForType(type); if (buildIndex !== -1) { tb = this._tb; const heapType = binaryenCAPI._TypeBuilderGetTempHeapType( tb, buildIndex, ); const refType = binaryenCAPI._TypeBuilderGetTempRefType( tb, heapType, true, ); if (isInfc) { this.infcObjTypeMap.set(type, refType); this.infcObjHeapTypeMap.set(type, heapType); } else { this.typeMap.set(type, refType); this.heapTypeMap.set(type, heapType); this.objTypeMap.set(metaInfo.name, refType); } } /* 1. traverse members */ /* currently vtable stores all member functions (without constructor) */ const methodTypeRefs = new Array(); methodTypeRefs.push(binaryen.i32); const vtableFuncs = new Array(); vtableFuncs.push( this.wasmComp.module.i32.const( this.wasmComp.generateMetaInfo(type), ), ); const fieldTypeRefs = new Array(); const fieldMuts = new Array(); const classInitValues = new Array(); this.parseObjectMembers( metaInfo, methodTypeRefs, vtableFuncs, fieldTypeRefs, fieldMuts, classInitValues, buildIndex, ); const methodPacked = new Array( methodTypeRefs.length, ).fill(Packed.Not); const methodMuts = new Array(methodTypeRefs.length).fill( false, ); let baseVtableWasmType: binaryen.Type | undefined; let baseWasmType: binaryen.Type | undefined; /* 2. generate needed structs */ /** TODO: Here we only support class extends class or class implement infc, * but not support class extends class implement infc, when ref.cast, wasm require declare subtype relationship, * So if both have, here we use super class as wasm suptype, that maybe cause error. */ /* vtable type */ if (type.super) { baseVtableWasmType = this.getWASMVtableHeapType(type.super); baseWasmType = this.getWASMHeapType(type.super); } else if (type.impl) { baseVtableWasmType = this.getWASMVtableHeapType(type.impl); baseWasmType = this.getWASMObjOriHeapType(type.impl); } else { baseVtableWasmType = baseVtableType.heapTypeRef; baseWasmType = baseStructType.heapTypeRef; } let vtableIndex = -1; let tb1 = binaryenCAPI._TypeBuilderCreate(1); if (buildIndex !== -1) { /** binaryen doesnt support yet */ tb1 = this._tb; vtableIndex = binaryenCAPI._TypeBuilderGetSize(tb1); this.auxTypeIndexMap.set(type, vtableIndex); binaryenCAPI._TypeBuilderGrow(tb1, 1); const heapType = binaryenCAPI._TypeBuilderGetTempHeapType( tb1, vtableIndex, ); const refType = binaryenCAPI._TypeBuilderGetTempRefType( tb1, heapType, true, ); this.vtableTypeMap.set(type, refType); this.vtableHeapTypeMap.set(type, heapType); } const vtableType = initStructType( methodTypeRefs, methodPacked, methodMuts, methodTypeRefs.length, true, vtableIndex, tb1, baseVtableWasmType, ); this.vtableTypeMap.set(type, vtableType.typeRef); this.vtableHeapTypeMap.set(type, vtableType.heapTypeRef); /* class type */ fieldTypeRefs.unshift(vtableType.typeRef); fieldMuts.unshift(false); const fieldPacked = new Array( fieldTypeRefs.length, ).fill(Packed.Not); const wasmClassType = initStructType( fieldTypeRefs, fieldPacked, fieldMuts, fieldTypeRefs.length, true, buildIndex, tb, baseWasmType, ); if (wasmClassType.heapTypeRef === 0) { throw Error(`failed to create class type for ${type.meta.name}`); } if (buildIndex === -1) { this.vtableTypeMap.set(type, vtableType.typeRef); this.vtableHeapTypeMap.set(type, vtableType.heapTypeRef); if (isInfc) { this.infcObjTypeMap.set(type, wasmClassType.typeRef); this.infcObjHeapTypeMap.set(type, wasmClassType.heapTypeRef); } else { /* vtable instance */ const vtableNameRef = UtilFuncs.getCString( `vt-inst${this.structHeapTypeCnt}`, ); const vtableInstance = this.createVtableInst( vtableNameRef, vtableType.typeRef, vtableType.heapTypeRef, vtableFuncs, ); /* this instance */ classInitValues.unshift(vtableInstance); const thisArg = binaryenCAPI._BinaryenStructNew( this.wasmComp.module.ptr, arrayToPtr(classInitValues).ptr, classInitValues.length, wasmClassType.heapTypeRef, ); this.vtableInstMap.set(type, vtableInstance); this.thisInstMap.set(type, thisArg); this.typeMap.set(type, wasmClassType.typeRef); this.heapTypeMap.set(type, wasmClassType.heapTypeRef); this.objTypeMap.set(metaInfo.name, wasmClassType.typeRef); } this.createCustomTypeName( `vt-struct${this.structHeapTypeCnt}`, vtableType.heapTypeRef, ); this.createCustomTypeName( `cls-struct${this.structHeapTypeCnt++}`, wasmClassType.heapTypeRef, ); } if ( buildIndex !== -1 && this.recStartElem && type === this.recStartElem ) { this.createRecObjectType(); } } createWASMSpecializeType(type: TypeParameterType) { const specialType = type.specialTypeArgument; if (specialType) { const specialTypeRef = this.getWASMValueType(specialType); this.typeMap.set(type, specialTypeRef); if (this.hasHeapType(specialType)) { const specialHeapTypeRef = this.getWASMValueHeapType(specialType); this.heapTypeMap.set(type, specialHeapTypeRef); } } else { this.typeMap.set(type, binaryen.anyref); } } createWASMGenericType(type: ValueType, typeArg: ValueType | null = null) { /* We treat generic as any for most cases, but for some builtin methods (e.g. Array.push), we want the generic type to be specialized for better performance */ if (typeArg) { const result: binaryenCAPI.TypeRef = this.getWASMValueType(type); this.typeMap.set(type, result); } else { this.typeMap.set(type, binaryen.anyref); } } hasHeapType(type: ValueType): boolean { if ( type.kind === ValueTypeKind.VOID || type.kind === ValueTypeKind.BOOLEAN || type.kind === ValueTypeKind.NUMBER || type.kind === ValueTypeKind.ANY || type.kind === ValueTypeKind.UNDEFINED ) { return false; } return true; } /** return heapTypeMap */ get heapType() { return this.heapTypeMap; } getWASMType(type: ValueType): binaryenCAPI.TypeRef { if (!this.typeMap.has(type) || needSpecialized(type)) { this.createWASMType(type); } return this.typeMap.get(type) as binaryenCAPI.TypeRef; } getWASMHeapType(type: ValueType): binaryenCAPI.HeapTypeRef { assert(this.hasHeapType(type), `${type} doesn't have heap type`); if (!this.heapTypeMap.has(type) || needSpecialized(type)) { this.createWASMType(type); } return this.heapTypeMap.get(type) as binaryenCAPI.HeapTypeRef; } getWASMValueType(type: ValueType): binaryenCAPI.TypeRef { if (!this.typeMap.has(type) || needSpecialized(type)) { this.createWASMType(type); } if (type instanceof FunctionType) { return this.closureStructTypeMap.get(type) as binaryenCAPI.TypeRef; } else { return this.typeMap.get(type) as binaryenCAPI.TypeRef; } } getWASMValueHeapType(type: ValueType): binaryenCAPI.HeapTypeRef { if (!this.typeMap.has(type) || needSpecialized(type)) { this.createWASMType(type); } if (type instanceof FunctionType) { return this.closureStructHeapTypeMap.get( type, ) as binaryenCAPI.HeapTypeRef; } else { return this.heapTypeMap.get(type) as binaryenCAPI.HeapTypeRef; } } getWASMFuncParamTypes(type: ValueType): binaryenCAPI.TypeRef[] { if (!this.funcParamTypesMap.has(type) || needSpecialized(type)) { this.createWASMType(type); } return this.funcParamTypesMap.get(type)!; } getWASMFuncOriParamTypes(type: ValueType): binaryenCAPI.TypeRef[] { if (!this.funcOriParamTypesMap.has(type) || needSpecialized(type)) { this.createWASMType(type); } return this.funcOriParamTypesMap.get(type)!; } getWASMArrayOriType(type: ValueType): binaryenCAPI.TypeRef { if (!this.oriArrayTypeMap.has(type) || needSpecialized(type)) { this.createWASMType(type); } return this.oriArrayTypeMap.get(type) as binaryenCAPI.TypeRef; } getWASMArrayOriHeapType(type: ValueType): binaryenCAPI.HeapTypeRef { if (!this.oriArrayHeapTypeMap.has(type) || needSpecialized(type)) { this.createWASMType(type); } return this.oriArrayHeapTypeMap.get(type) as binaryenCAPI.HeapTypeRef; } getWASMObjOriType(type: ValueType): binaryenCAPI.TypeRef { if (!this.infcObjTypeMap.has(type) || needSpecialized(type)) { this.createWASMClassType(type as ObjectType, true); } return this.infcObjTypeMap.get(type) as binaryenCAPI.TypeRef; } getWASMObjOriHeapType(type: ValueType): binaryenCAPI.HeapTypeRef { if (!this.infcObjHeapTypeMap.has(type) || needSpecialized(type)) { this.createWASMClassType(type as ObjectType, true); } return this.infcObjHeapTypeMap.get(type) as binaryenCAPI.TypeRef; } getWASMVtableType(type: ValueType): binaryenCAPI.TypeRef { if (!this.vtableTypeMap.has(type) || needSpecialized(type)) { this.createWASMType(type); } return this.vtableTypeMap.get(type) as binaryenCAPI.TypeRef; } getWASMVtableHeapType(type: ValueType): binaryenCAPI.HeapTypeRef { if (!this.vtableHeapTypeMap.has(type) || needSpecialized(type)) { this.createWASMType(type); } return this.vtableHeapTypeMap.get(type) as binaryenCAPI.HeapTypeRef; } getWASMStaticFieldsType(type: ValueType): binaryenCAPI.TypeRef { if (!this.staticFieldsTypeMap.has(type)) { this.createWASMType(type); } return this.staticFieldsTypeMap.get(type) as binaryenCAPI.TypeRef; } getWASMStaticFieldsHeapType(type: ValueType): binaryenCAPI.HeapTypeRef { if (!this.staticFieldsHeapTypeMap.has(type)) { this.createWASMType(type); } return this.staticFieldsHeapTypeMap.get( type, ) as binaryenCAPI.HeapTypeRef; } getWASMVtableInst(type: ValueType): binaryen.ExpressionRef { if (!this.vtableInstMap.has(type) || needSpecialized(type)) { this.createWASMObjectType(type as ObjectType); } return this.vtableInstMap.get(type) as binaryen.ExpressionRef; } getWASMThisInst(type: ValueType): binaryen.ExpressionRef { if (!this.thisInstMap.has(type) || needSpecialized(type)) { this.createWASMType(type); } return this.thisInstMap.get(type) as binaryen.ExpressionRef; } updateStaticFields(type: ObjectType) { const metaInfo = type.meta; const name = metaInfo.name + '|static_fields'; this.wasmComp.globalInitFuncCtx.insert( binaryenCAPI._BinaryenGlobalSet( this.wasmComp.module.ptr, UtilFuncs.getCString(name), binaryenCAPI._BinaryenStructNew( this.wasmComp.module.ptr, arrayToPtr([]).ptr, 0, this.getWASMStaticFieldsHeapType(type), ), ), ); let staticFieldIdx = 0; const staticFields = binaryenCAPI._BinaryenGlobalGet( this.wasmComp.module.ptr, UtilFuncs.getCString(name), this.getWASMStaticFieldsType(type), ); for (const member of metaInfo.members) { if (member.type === MemberType.FIELD && member.isStaic) { const initValue = member.staticFieldInitValue!; const memberType = member.valueType; const valueType = initValue.type; /** for Map/Set, it's any type */ let isInitFallBackType = false; if (valueType instanceof ObjectType) { const name = valueType.meta.name; isInitFallBackType = name == BuiltinNames.MAP || name == BuiltinNames.SET; } let isMemFallBackType = false; if (memberType instanceof ObjectType) { const name = memberType.meta.name; isMemFallBackType = name == BuiltinNames.MAP || name == BuiltinNames.SET; } const curFuncCtx = this.wasmComp.currentFuncCtx; this.wasmComp.currentFuncCtx = this.wasmComp.globalInitFuncCtx; let wasmInitvalue = this.wasmComp.wasmExprComp.wasmExprGen(initValue); this.wasmComp.currentFuncCtx = curFuncCtx; if ( memberType.kind === ValueTypeKind.ANY && valueType.kind !== ValueTypeKind.ANY && !isInitFallBackType ) { wasmInitvalue = FunctionalFuncs.boxToAny( this.wasmComp.module, wasmInitvalue, initValue, ); } if ( memberType.kind !== ValueTypeKind.ANY && valueType.kind === ValueTypeKind.ANY && !isMemFallBackType ) { wasmInitvalue = FunctionalFuncs.unboxAny( this.wasmComp.module, wasmInitvalue, valueType.kind, this.getWASMType(valueType), ); } const res = binaryenCAPI._BinaryenStructSet( this.wasmComp.module.ptr, staticFieldIdx, staticFields, wasmInitvalue, ); this.wasmComp.globalInitFuncCtx.insert(res); staticFieldIdx++; } } this.staticFieldsUpdateMap.set(type, true); } private createCustomTypeName( name: string, heapTypeRef: binaryenCAPI.HeapTypeRef, ) { binaryenCAPI._BinaryenModuleSetTypeName( this.wasmComp.module.ptr, heapTypeRef, UtilFuncs.getCString(name), ); } private createRecObjectType() { const size = binaryenCAPI._TypeBuilderGetSize(this._tb); binaryenCAPI._TypeBuilderCreateRecGroup(this._tb, 0, size); const builtHeapType: binaryenCAPI.HeapTypeRef[] = new Array(size); const builtHeapTypePtr = arrayToPtr(builtHeapType); const res = binaryenCAPI._TypeBuilderBuildAndDispose( this._tb, builtHeapTypePtr.ptr, 0, 0, ); if (!res) { throw new Error('create rec group failed'); } const baseAddr = ptrToArray(builtHeapTypePtr); this.typeToBuilderIdxMap.forEach((index, type) => { const refType = binaryenCAPI._BinaryenTypeFromHeapType( baseAddr[index], true, ); const heapType = binaryenCAPI._BinaryenTypeGetHeapType(refType); if (type instanceof ArrayType) { this.oriArrayTypeMap.set(type, refType); this.oriArrayHeapTypeMap.set(type, heapType); const structArrIdx = this.auxTypeIndexMap.get(type); const structArrRefType = binaryenCAPI._BinaryenTypeFromHeapType( baseAddr[structArrIdx!], true, ); const structArrHeapType = binaryenCAPI._BinaryenTypeGetHeapType(structArrRefType); this.typeMap.set(type, structArrRefType); this.heapTypeMap.set(type, structArrHeapType); this.createCustomTypeName( `array-struct${this.arrayHeapTypeCnt++}`, structArrHeapType, ); this.createCustomTypeName( `array${this.arrayTypeCnt++}`, heapType, ); } else if (type instanceof ObjectType) { const vtableIdx = this.auxTypeIndexMap.get(type); const vtableRef = binaryenCAPI._BinaryenTypeFromHeapType( baseAddr[vtableIdx!], true, ); const vtableHeapType = binaryenCAPI._BinaryenTypeGetHeapType(vtableRef); this.vtableTypeMap.set(type, vtableRef); this.vtableHeapTypeMap.set(type, vtableHeapType); this.createCustomTypeName( `vt-struct${this.structHeapTypeCnt}`, vtableHeapType, ); if (type.meta.isInterface) { this.infcObjTypeMap.set(type, refType); this.infcObjHeapTypeMap.set(type, heapType); } else { this.typeMap.set(type, refType); this.heapTypeMap.set(type, heapType); this.objTypeMap.set(type.meta.name, refType); } this.createCustomTypeName( `cls-struct${this.structHeapTypeCnt++}`, heapType, ); } else if (type instanceof FunctionType) { const closureIndex = this.auxTypeIndexMap.get(type); const closureRefType = binaryenCAPI._BinaryenTypeFromHeapType( baseAddr[closureIndex!], true, ); const closureHeapType = binaryenCAPI._BinaryenTypeGetHeapType(closureRefType); this.closureStructTypeMap.set(type, closureRefType); this.closureStructHeapTypeMap.set(type, closureHeapType); this.typeMap.set(type, refType); this.heapTypeMap.set(type, heapType); this.createCustomTypeName( `function${this.funcHeapTypeCnt}`, heapType, ); this.createCustomTypeName( `closure${this.funcHeapTypeCnt++}`, closureHeapType, ); } }); const builderMap = this.typeToBuilderIdxMap; for (const type of builderMap.keys()) { /** create function parameter types */ if (type instanceof FunctionType) { const paramTypes = type.argumentsType; const paramWASMTypes = new Array(); const oriParamWASMTypes = new Array(); for (let i = 0; i < type.envParamLen; ++i) { paramWASMTypes.push(emptyStructType.typeRef); } for (let i = 0; i < paramTypes.length; ++i) { const paramTypeRef = this.getWASMValueType(paramTypes[i]); paramWASMTypes.push(paramTypeRef); oriParamWASMTypes.push(paramTypeRef); } this.funcParamTypesMap.set(type, paramWASMTypes); this.funcOriParamTypesMap.set(type, oriParamWASMTypes); } else if (type instanceof ArrayType) { // } else if (type instanceof ObjectType) { const methodTypeRefs = new Array(); methodTypeRefs.push(binaryen.i32); const vtableFuncs = new Array(); vtableFuncs.push( this.wasmComp.module.i32.const( this.wasmComp.generateMetaInfo(type), ), ); const fieldTypeRefs = new Array(); const fieldMuts = new Array(); const classInitValues = new Array(); this.parseObjectMembers( type.meta, methodTypeRefs, vtableFuncs, fieldTypeRefs, fieldMuts, classInitValues, -1, ); /** static fields */ /* vtable instance */ const vtableNameRef = UtilFuncs.getCString( `vt-inst${this.structHeapTypeCnt++}`, ); const vtableTypeRef = this.getWASMVtableType(type); const vtableHeapType = this.getWASMVtableHeapType(type); const vtableInstance = this.createVtableInst( vtableNameRef, vtableTypeRef, vtableHeapType, vtableFuncs, ); /* this instance */ classInitValues.unshift(vtableInstance); const thisArg = binaryenCAPI._BinaryenStructNew( this.wasmComp.module.ptr, arrayToPtr(classInitValues).ptr, classInitValues.length, this.getWASMHeapType(type), ); this.vtableInstMap.set(type, vtableInstance); this.thisInstMap.set(type, thisArg); } } } private isInRecGroup(type: ValueType) { if (type instanceof FunctionType) { const params = type.argumentsType; for (let i = 0; i < params.length; i++) { if (this.isInRecGroup(params[i])) { return true; } } if (this.isInRecGroup(type.returnType)) { return true; } } else if (type instanceof ArrayType) { if (this.isInRecGroup(type.element)) { return true; } } else if (type instanceof ObjectType) { return this.typeToBuilderIdxMap.has(type); } return false; } private createTbIndexForType(type: ValueType) { let index = -1; if (this.isInRecGroup(type)) { if (this.typeToBuilderIdxMap.has(type)) { index = this.typeToBuilderIdxMap.get(type)!; } else { index = binaryenCAPI._TypeBuilderGetSize(this._tb); this.typeToBuilderIdxMap.set(type, index); binaryenCAPI._TypeBuilderGrow(this._tb, 1); } } return index; } private getExistWasmArrType(arrayType: ArrayType) { for (const alreadyParsedType of this.typeMap.keys()) { if (alreadyParsedType instanceof ArrayType) { if (alreadyParsedType.toString() === arrayType.toString()) { this.oriArrayTypeMap.set( arrayType, this.oriArrayTypeMap.get(alreadyParsedType)!, ); this.oriArrayHeapTypeMap.set( arrayType, this.oriArrayHeapTypeMap.get(alreadyParsedType)!, ); this.typeMap.set( arrayType, this.typeMap.get(alreadyParsedType)!, ); this.heapTypeMap.set( arrayType, this.heapTypeMap.get(alreadyParsedType)!, ); return true; } } } return false; } private parseObjectMembers( metaInfo: ObjectDescription, methodTypeRefs: binaryenCAPI.TypeRef[], vtableFuncs: binaryen.ExpressionRef[], fieldTypeRefs: binaryenCAPI.TypeRef[], fieldMuts: boolean[], classInitValues: binaryen.ExpressionRef[], buildIndex: number, ) { for (const member of metaInfo.members) { if (member.type === MemberType.METHOD) { let methodMangledName = UtilFuncs.getFuncName( metaInfo.name, member.name, ); if (!metaInfo.isLiteral) { methodMangledName = this.wasmComp.getMethodMangledName( member, metaInfo, ); if ( BuiltinNames.genericBuiltinMethods.includes( methodMangledName, ) ) { continue; } } const methodTypeRef = this.getWASMType(member.valueType); methodTypeRefs.push(methodTypeRef); if (buildIndex === -1) { vtableFuncs.push( this.wasmComp.module.ref.func( methodMangledName, methodTypeRef, ), ); } } else if (member.type === MemberType.ACCESSOR) { /* Put accessor to vtable, getter first */ if (member.hasGetter) { let methodMangledName = (member.getter as VarValue) .index as string; if (!metaInfo.isLiteral) { methodMangledName = this.wasmComp.getMethodMangledName( member, metaInfo, 0, ); } const methodType = this.getWASMType( (member.getter as VarValue).type, ); methodTypeRefs.push(methodType); if (buildIndex === -1) { vtableFuncs.push( this.wasmComp.module.ref.func( methodMangledName, methodType, ), ); } } else { const getterTypeRef = binaryenCAPI._BinaryenTypeFuncref(); methodTypeRefs.push(getterTypeRef); if (buildIndex === -1) { vtableFuncs.push( binaryenCAPI._BinaryenRefNull( this.wasmComp.module.ptr, getterTypeRef, ), ); } } if (member.hasSetter) { let methodMangledName = (member.setter as VarValue) .index as string; if (!metaInfo.isLiteral) { methodMangledName = this.wasmComp.getMethodMangledName( member, metaInfo, 1, ); } const methodType = this.getWASMType( (member.setter as VarValue).type, ); methodTypeRefs.push(methodType); if (buildIndex === -1) { vtableFuncs.push( this.wasmComp.module.ref.func( methodMangledName, methodType, ), ); } } else { const setterTypeRef = binaryenCAPI._BinaryenTypeFuncref(); methodTypeRefs.push(setterTypeRef); if (buildIndex === -1) { vtableFuncs.push( binaryenCAPI._BinaryenRefNull( this.wasmComp.module.ptr, setterTypeRef, ), ); } } } else if (member.type === MemberType.FIELD) { let defaultValue = FunctionalFuncs.getVarDefaultValue( this.wasmComp.module, member.valueType, ); if (member.valueType.kind === ValueTypeKind.ANY) { defaultValue = FunctionalFuncs.generateDynUndefined( this.wasmComp.module, ); } if ( member.valueType instanceof UnionType && member.valueType.types.has(Primitive.Undefined) ) { defaultValue = FunctionalFuncs.generateDynUndefined( this.wasmComp.module, ); } fieldTypeRefs.push(this.getWASMValueType(member.valueType)); if ((member.modifiers & MemberModifier.READONLY) !== 0) { fieldMuts.push(false); } else { fieldMuts.push(true); } classInitValues.push(defaultValue); } } } private createTypeForStaticFields( typeRefs: binaryenCAPI.TypeRef[], type: ObjectType, ) { if (typeRefs.length === 0) { return; } const staticPacked = new Array( typeRefs.length, ).fill(Packed.Not); const staticMuts = new Array(typeRefs.length).fill(true); const staticStructType = initStructType( typeRefs, staticPacked, staticMuts, typeRefs.length, true, -1, binaryenCAPI._TypeBuilderCreate(1), ); this.createCustomTypeName( `static-struct${this.structHeapTypeCnt++}`, staticStructType.heapTypeRef, ); const name = type.meta.name + '|static_fields'; /** clazz meta */ binaryenCAPI._BinaryenAddGlobal( this.wasmComp.module.ptr, UtilFuncs.getCString(name), staticStructType.typeRef, true, this.wasmComp.module.ref.null( binaryenCAPI._BinaryenTypeStructref(), ), ); this.staticFieldsTypeMap.set(type, staticStructType.typeRef); this.staticFieldsHeapTypeMap.set(type, staticStructType.heapTypeRef); this.staticFieldsUpdateMap.set(type, false); } private createStaticFields(type: ObjectType) { const staticFieldsTypeRefs = new Array(); const metaInfo = type.meta; for (const member of metaInfo.members) { if (member.type === MemberType.FIELD && member.isStaic) { staticFieldsTypeRefs.push( this.getWASMValueType(member.valueType), ); } } this.createTypeForStaticFields(staticFieldsTypeRefs, type); } private createVtableInst( vtableNameRef: binaryen.ExpressionRef, vtableTypeRef: binaryenCAPI.TypeRef, vtableHeapType: binaryenCAPI.HeapTypeRef, methods: binaryen.ExpressionRef[], ) { binaryenCAPI._BinaryenAddGlobal( this.wasmComp.module.ptr, vtableNameRef, vtableTypeRef, true, binaryenCAPI._BinaryenRefNull( this.wasmComp.module.ptr, vtableTypeRef, ), ); const initVtableInst = binaryenCAPI._BinaryenGlobalSet( this.wasmComp.module.ptr, vtableNameRef, binaryenCAPI._BinaryenStructNew( this.wasmComp.module.ptr, arrayToPtr(methods).ptr, methods.length, vtableHeapType, ), ); this.wasmComp.globalInitFuncCtx.insert(initVtableInst); return binaryenCAPI._BinaryenGlobalGet( this.wasmComp.module.ptr, vtableNameRef, vtableTypeRef, ); } } ================================================ FILE: src/backend/index.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { ParserContext } from '../frontend.js'; import { UtilFuncs } from './binaryen/utils.js'; export { ParserContext } from '../frontend.js'; export abstract class Ts2wasmBackend { constructor(protected parserContext: ParserContext) {} public abstract codegen(options?: any): void; public abstract emitBinary(options?: any): Uint8Array; public abstract emitText(options?: any): string; public abstract emitSourceMap(name: string): string; public abstract dispose(): void; } export interface SegmentInfo { data: Uint8Array; offset: number; } export class DataSegmentContext { static readonly reservedSpace: number = 1024; currentOffset; stringOffsetMap; /* cache */ metaMap; dataArray: Array = []; constructor() { /* Reserve 1024 bytes at beggining */ this.currentOffset = DataSegmentContext.reservedSpace; this.stringOffsetMap = new Map(); this.metaMap = new Map(); } addData(data: Uint8Array, alignment = 4) { /* there is no efficient approach to cache the data buffer, currently we don't cache it */ const offset = this.currentOffset; this.currentOffset += data.length; this.dataArray.push({ data: data, offset: offset, }); if (alignment > 0) { /* alignment */ this.currentOffset = (this.currentOffset + (alignment - 1)) & ~(alignment - 1); } return offset; } addString(str: string, alignment = 4) { if (this.stringOffsetMap.has(str)) { /* Re-use the string to save space */ return this.stringOffsetMap.get(str)!; } const offset = this.currentOffset; this.stringOffsetMap.set(str, offset); const utf8Str = UtilFuncs.utf16ToUtf8(str); this.currentOffset += utf8Str.length + 1; const buffer = new Uint8Array(utf8Str.length + 1); for (let i = 0; i < utf8Str.length; i++) { const byte = utf8Str.charCodeAt(i); buffer[i] = byte; } buffer[utf8Str.length] = 0; this.dataArray.push({ data: buffer, offset: offset, }); if (alignment > 0) { /* alignment */ this.currentOffset = (this.currentOffset + (alignment - 1)) & ~(alignment - 1); } return offset; } generateSegment(): SegmentInfo | null { const offset = DataSegmentContext.reservedSpace; const size = this.currentOffset - offset; if (this.dataArray.length === 0) { return null; } const data = new Uint8Array(size); this.dataArray.forEach((info) => { for (let i = 0; i < info.data.length; i++) { const targetOffset = i + info.offset - DataSegmentContext.reservedSpace; data[targetOffset] = info.data[i]; } }); return { offset: offset, data: data, }; } getDataEnd(): number { return this.currentOffset; } } ================================================ FILE: src/dump_ast.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import ts from 'typescript'; export function operatorString(kind: ts.BinaryOperator) { switch (kind) { case ts.SyntaxKind.FirstAssignment: return '='; case ts.SyntaxKind.PlusEqualsToken: return '+='; case ts.SyntaxKind.MinusEqualsToken: return '-='; case ts.SyntaxKind.AsteriskAsteriskEqualsToken: return '**='; case ts.SyntaxKind.AsteriskEqualsToken: return '*='; case ts.SyntaxKind.SlashEqualsToken: return '/='; case ts.SyntaxKind.PercentEqualsToken: return '%='; case ts.SyntaxKind.AmpersandEqualsToken: return '&='; case ts.SyntaxKind.BarEqualsToken: return '|='; case ts.SyntaxKind.CaretEqualsToken: return '^='; case ts.SyntaxKind.LessThanLessThanEqualsToken: return '<<='; case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: return '>>>='; case ts.SyntaxKind.GreaterThanGreaterThanEqualsToken: return '>>='; case ts.SyntaxKind.AsteriskAsteriskToken: return '**'; case ts.SyntaxKind.AsteriskToken: return '*'; case ts.SyntaxKind.SlashToken: return '/'; case ts.SyntaxKind.PercentToken: return '%'; case ts.SyntaxKind.PlusToken: return '+'; case ts.SyntaxKind.MinusToken: return '-'; case ts.SyntaxKind.CommaToken: return ','; case ts.SyntaxKind.LessThanLessThanToken: return '<<'; case ts.SyntaxKind.GreaterThanGreaterThanToken: return '>>'; case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken: return '<<<'; case ts.SyntaxKind.LessThanToken: return '<'; case ts.SyntaxKind.LessThanEqualsToken: return '<='; case ts.SyntaxKind.GreaterThanToken: return '>'; case ts.SyntaxKind.GreaterThanEqualsToken: return '>='; case ts.SyntaxKind.InstanceOfKeyword: return 'instance of'; case ts.SyntaxKind.InKeyword: return 'in'; case ts.SyntaxKind.EqualsEqualsToken: return '=='; case ts.SyntaxKind.EqualsEqualsEqualsToken: return '==='; case ts.SyntaxKind.ExclamationEqualsEqualsToken: return '!=='; case ts.SyntaxKind.ExclamationEqualsToken: return '!='; case ts.SyntaxKind.AmpersandToken: return '&'; case ts.SyntaxKind.BarToken: return '|'; case ts.SyntaxKind.CaretToken: return '^'; case ts.SyntaxKind.AmpersandAmpersandToken: return '&&'; case ts.SyntaxKind.BarBarToken: return '||'; case ts.SyntaxKind.QuestionQuestionToken: return '??'; } return ts.SyntaxKind[kind]; } export function nodeToString(node: ts.Node): string { let s = ts.SyntaxKind[node.kind]; switch (node.kind) { case ts.SyntaxKind.Unknown: break; case ts.SyntaxKind.EndOfFileToken: break; case ts.SyntaxKind.SingleLineCommentTrivia: break; case ts.SyntaxKind.MultiLineCommentTrivia: break; case ts.SyntaxKind.NewLineTrivia: break; case ts.SyntaxKind.WhitespaceTrivia: break; case ts.SyntaxKind.ShebangTrivia: break; case ts.SyntaxKind.ConflictMarkerTrivia: break; case ts.SyntaxKind.NumericLiteral: case ts.SyntaxKind.BigIntLiteral: case ts.SyntaxKind.StringLiteral: s = s + ' "' + (node as ts.LiteralExpression).text + '"'; break; case ts.SyntaxKind.JsxText: break; case ts.SyntaxKind.JsxTextAllWhiteSpaces: break; case ts.SyntaxKind.RegularExpressionLiteral: break; case ts.SyntaxKind.NoSubstitutionTemplateLiteral: break; case ts.SyntaxKind.TemplateHead: case ts.SyntaxKind.TemplateMiddle: case ts.SyntaxKind.TemplateTail: s = s + ' "' + (node as ts.LiteralLikeNode).text + '"'; break; case ts.SyntaxKind.OpenBraceToken: break; case ts.SyntaxKind.CloseBraceToken: break; case ts.SyntaxKind.OpenParenToken: break; case ts.SyntaxKind.CloseParenToken: break; case ts.SyntaxKind.OpenBracketToken: break; case ts.SyntaxKind.CloseBracketToken: break; case ts.SyntaxKind.DotToken: break; case ts.SyntaxKind.DotDotDotToken: break; case ts.SyntaxKind.SemicolonToken: break; case ts.SyntaxKind.CommaToken: break; case ts.SyntaxKind.QuestionDotToken: break; case ts.SyntaxKind.LessThanToken: break; case ts.SyntaxKind.LessThanSlashToken: break; case ts.SyntaxKind.GreaterThanToken: break; case ts.SyntaxKind.LessThanEqualsToken: break; case ts.SyntaxKind.GreaterThanEqualsToken: break; case ts.SyntaxKind.EqualsEqualsToken: break; case ts.SyntaxKind.ExclamationEqualsToken: break; case ts.SyntaxKind.EqualsEqualsEqualsToken: break; case ts.SyntaxKind.ExclamationEqualsEqualsToken: break; case ts.SyntaxKind.EqualsGreaterThanToken: break; case ts.SyntaxKind.PlusToken: break; case ts.SyntaxKind.MinusToken: break; case ts.SyntaxKind.AsteriskToken: break; case ts.SyntaxKind.AsteriskAsteriskToken: break; case ts.SyntaxKind.SlashToken: break; case ts.SyntaxKind.PercentToken: break; case ts.SyntaxKind.PlusPlusToken: break; case ts.SyntaxKind.MinusMinusToken: break; case ts.SyntaxKind.LessThanLessThanToken: break; case ts.SyntaxKind.GreaterThanGreaterThanToken: break; case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken: break; case ts.SyntaxKind.AmpersandToken: break; case ts.SyntaxKind.BarToken: break; case ts.SyntaxKind.CaretToken: break; case ts.SyntaxKind.ExclamationToken: break; case ts.SyntaxKind.TildeToken: break; case ts.SyntaxKind.AmpersandAmpersandToken: break; case ts.SyntaxKind.BarBarToken: break; case ts.SyntaxKind.QuestionToken: break; case ts.SyntaxKind.ColonToken: break; case ts.SyntaxKind.AtToken: break; case ts.SyntaxKind.QuestionQuestionToken: break; /** Only the JSDoc scanner produces BacktickToken. The normal scanner produces NoSubstitutionTemplateLiteral and related kinds. */ case ts.SyntaxKind.BacktickToken: break; case ts.SyntaxKind.EqualsToken: s = s + ' ' + operatorString(node.kind); break; case ts.SyntaxKind.PlusEqualsToken: break; case ts.SyntaxKind.MinusEqualsToken: break; case ts.SyntaxKind.AsteriskEqualsToken: break; case ts.SyntaxKind.AsteriskAsteriskEqualsToken: break; case ts.SyntaxKind.SlashEqualsToken: break; case ts.SyntaxKind.PercentEqualsToken: break; case ts.SyntaxKind.LessThanLessThanEqualsToken: break; case ts.SyntaxKind.GreaterThanGreaterThanEqualsToken: break; case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: break; case ts.SyntaxKind.AmpersandEqualsToken: break; case ts.SyntaxKind.BarEqualsToken: break; case ts.SyntaxKind.CaretEqualsToken: break; case ts.SyntaxKind.Identifier: s = s + " '" + (node as ts.Identifier).escapedText + "'"; break; case ts.SyntaxKind.PrivateIdentifier: break; case ts.SyntaxKind.BreakKeyword: break; case ts.SyntaxKind.CaseKeyword: break; case ts.SyntaxKind.CatchKeyword: break; case ts.SyntaxKind.ClassKeyword: break; case ts.SyntaxKind.ConstKeyword: break; case ts.SyntaxKind.ContinueKeyword: break; case ts.SyntaxKind.DebuggerKeyword: break; case ts.SyntaxKind.DefaultKeyword: break; case ts.SyntaxKind.DeleteKeyword: break; case ts.SyntaxKind.DoKeyword: break; case ts.SyntaxKind.ElseKeyword: break; case ts.SyntaxKind.EnumKeyword: break; case ts.SyntaxKind.ExportKeyword: break; case ts.SyntaxKind.ExtendsKeyword: break; case ts.SyntaxKind.FalseKeyword: break; case ts.SyntaxKind.FinallyKeyword: break; case ts.SyntaxKind.ForKeyword: break; case ts.SyntaxKind.FunctionKeyword: break; case ts.SyntaxKind.IfKeyword: break; case ts.SyntaxKind.ImportKeyword: break; case ts.SyntaxKind.InKeyword: break; case ts.SyntaxKind.InstanceOfKeyword: break; case ts.SyntaxKind.NewKeyword: break; case ts.SyntaxKind.NullKeyword: break; case ts.SyntaxKind.ReturnKeyword: break; case ts.SyntaxKind.SuperKeyword: break; case ts.SyntaxKind.SwitchKeyword: break; case ts.SyntaxKind.ThisKeyword: break; case ts.SyntaxKind.ThrowKeyword: break; case ts.SyntaxKind.TrueKeyword: break; case ts.SyntaxKind.TryKeyword: break; case ts.SyntaxKind.TypeOfKeyword: break; case ts.SyntaxKind.VarKeyword: break; case ts.SyntaxKind.VoidKeyword: break; case ts.SyntaxKind.WhileKeyword: break; case ts.SyntaxKind.WithKeyword: break; case ts.SyntaxKind.ImplementsKeyword: break; case ts.SyntaxKind.InterfaceKeyword: break; case ts.SyntaxKind.LetKeyword: break; case ts.SyntaxKind.PackageKeyword: break; case ts.SyntaxKind.PrivateKeyword: break; case ts.SyntaxKind.ProtectedKeyword: break; case ts.SyntaxKind.PublicKeyword: break; case ts.SyntaxKind.StaticKeyword: break; case ts.SyntaxKind.YieldKeyword: break; case ts.SyntaxKind.AbstractKeyword: break; case ts.SyntaxKind.AsKeyword: break; case ts.SyntaxKind.AssertsKeyword: break; case ts.SyntaxKind.AnyKeyword: break; case ts.SyntaxKind.AsyncKeyword: break; case ts.SyntaxKind.AwaitKeyword: break; case ts.SyntaxKind.BooleanKeyword: break; case ts.SyntaxKind.ConstructorKeyword: break; case ts.SyntaxKind.DeclareKeyword: break; case ts.SyntaxKind.GetKeyword: break; case ts.SyntaxKind.InferKeyword: break; case ts.SyntaxKind.IsKeyword: break; case ts.SyntaxKind.KeyOfKeyword: break; case ts.SyntaxKind.ModuleKeyword: break; case ts.SyntaxKind.NamespaceKeyword: break; case ts.SyntaxKind.NeverKeyword: break; case ts.SyntaxKind.ReadonlyKeyword: break; case ts.SyntaxKind.RequireKeyword: break; case ts.SyntaxKind.NumberKeyword: break; case ts.SyntaxKind.ObjectKeyword: break; case ts.SyntaxKind.SetKeyword: break; case ts.SyntaxKind.StringKeyword: break; case ts.SyntaxKind.SymbolKeyword: break; case ts.SyntaxKind.TypeKeyword: break; case ts.SyntaxKind.UndefinedKeyword: break; case ts.SyntaxKind.UniqueKeyword: break; case ts.SyntaxKind.UnknownKeyword: break; case ts.SyntaxKind.FromKeyword: break; case ts.SyntaxKind.GlobalKeyword: break; case ts.SyntaxKind.BigIntKeyword: break; case ts.SyntaxKind.OfKeyword: break; case ts.SyntaxKind.QualifiedName: break; case ts.SyntaxKind.ComputedPropertyName: break; case ts.SyntaxKind.TypeParameter: break; case ts.SyntaxKind.Parameter: break; case ts.SyntaxKind.Decorator: break; case ts.SyntaxKind.PropertySignature: break; case ts.SyntaxKind.PropertyDeclaration: break; case ts.SyntaxKind.MethodSignature: break; case ts.SyntaxKind.MethodDeclaration: break; case ts.SyntaxKind.Constructor: break; case ts.SyntaxKind.GetAccessor: break; case ts.SyntaxKind.SetAccessor: break; case ts.SyntaxKind.CallSignature: break; case ts.SyntaxKind.ConstructSignature: break; case ts.SyntaxKind.IndexSignature: break; case ts.SyntaxKind.TypePredicate: break; case ts.SyntaxKind.TypeReference: break; case ts.SyntaxKind.FunctionType: break; case ts.SyntaxKind.ConstructorType: break; case ts.SyntaxKind.TypeQuery: break; case ts.SyntaxKind.TypeLiteral: break; case ts.SyntaxKind.ArrayType: break; case ts.SyntaxKind.TupleType: break; case ts.SyntaxKind.OptionalType: break; case ts.SyntaxKind.RestType: break; case ts.SyntaxKind.UnionType: break; case ts.SyntaxKind.IntersectionType: break; case ts.SyntaxKind.ConditionalType: break; case ts.SyntaxKind.InferType: break; case ts.SyntaxKind.ParenthesizedType: break; case ts.SyntaxKind.ThisType: break; case ts.SyntaxKind.TypeOperator: break; case ts.SyntaxKind.IndexedAccessType: break; case ts.SyntaxKind.MappedType: break; case ts.SyntaxKind.LiteralType: break; case ts.SyntaxKind.ImportType: break; case ts.SyntaxKind.ObjectBindingPattern: break; case ts.SyntaxKind.ArrayBindingPattern: break; case ts.SyntaxKind.BindingElement: break; case ts.SyntaxKind.ArrayLiteralExpression: break; case ts.SyntaxKind.ObjectLiteralExpression: break; case ts.SyntaxKind.PropertyAccessExpression: break; case ts.SyntaxKind.ElementAccessExpression: break; case ts.SyntaxKind.CallExpression: break; case ts.SyntaxKind.NewExpression: break; case ts.SyntaxKind.TaggedTemplateExpression: break; case ts.SyntaxKind.TypeAssertionExpression: break; case ts.SyntaxKind.ParenthesizedExpression: break; case ts.SyntaxKind.FunctionExpression: break; case ts.SyntaxKind.ArrowFunction: break; case ts.SyntaxKind.DeleteExpression: break; case ts.SyntaxKind.TypeOfExpression: break; case ts.SyntaxKind.VoidExpression: break; case ts.SyntaxKind.AwaitExpression: break; case ts.SyntaxKind.PrefixUnaryExpression: break; case ts.SyntaxKind.PostfixUnaryExpression: break; case ts.SyntaxKind.BinaryExpression: s = s + " operator:'" + operatorString( (node as ts.BinaryExpression).operatorToken.kind, ) + "'"; break; case ts.SyntaxKind.ConditionalExpression: break; case ts.SyntaxKind.TemplateExpression: break; case ts.SyntaxKind.YieldExpression: break; case ts.SyntaxKind.SpreadElement: break; case ts.SyntaxKind.ClassExpression: break; case ts.SyntaxKind.OmittedExpression: break; case ts.SyntaxKind.ExpressionWithTypeArguments: break; case ts.SyntaxKind.AsExpression: break; case ts.SyntaxKind.NonNullExpression: break; case ts.SyntaxKind.MetaProperty: break; case ts.SyntaxKind.SyntheticExpression: break; case ts.SyntaxKind.TemplateSpan: break; case ts.SyntaxKind.SemicolonClassElement: break; case ts.SyntaxKind.Block: break; case ts.SyntaxKind.EmptyStatement: break; case ts.SyntaxKind.VariableStatement: s = 'VariableStatement'; break; case ts.SyntaxKind.ExpressionStatement: break; case ts.SyntaxKind.IfStatement: break; case ts.SyntaxKind.DoStatement: break; case ts.SyntaxKind.WhileStatement: break; case ts.SyntaxKind.ForStatement: break; case ts.SyntaxKind.ForInStatement: break; case ts.SyntaxKind.ForOfStatement: break; case ts.SyntaxKind.ContinueStatement: break; case ts.SyntaxKind.BreakStatement: break; case ts.SyntaxKind.ReturnStatement: break; case ts.SyntaxKind.WithStatement: break; case ts.SyntaxKind.SwitchStatement: break; case ts.SyntaxKind.LabeledStatement: break; case ts.SyntaxKind.ThrowStatement: break; case ts.SyntaxKind.TryStatement: break; case ts.SyntaxKind.DebuggerStatement: break; case ts.SyntaxKind.VariableDeclaration: break; case ts.SyntaxKind.VariableDeclarationList: break; case ts.SyntaxKind.FunctionDeclaration: break; case ts.SyntaxKind.ClassDeclaration: break; case ts.SyntaxKind.InterfaceDeclaration: break; case ts.SyntaxKind.TypeAliasDeclaration: break; case ts.SyntaxKind.EnumDeclaration: break; case ts.SyntaxKind.ModuleDeclaration: break; case ts.SyntaxKind.ModuleBlock: break; case ts.SyntaxKind.CaseBlock: break; case ts.SyntaxKind.NamespaceExportDeclaration: break; case ts.SyntaxKind.ImportEqualsDeclaration: break; case ts.SyntaxKind.ImportDeclaration: break; case ts.SyntaxKind.ImportClause: break; case ts.SyntaxKind.NamespaceImport: break; case ts.SyntaxKind.NamedImports: break; case ts.SyntaxKind.ImportSpecifier: break; case ts.SyntaxKind.ExportAssignment: break; case ts.SyntaxKind.ExportDeclaration: break; case ts.SyntaxKind.NamedExports: break; case ts.SyntaxKind.NamespaceExport: break; case ts.SyntaxKind.ExportSpecifier: break; case ts.SyntaxKind.MissingDeclaration: break; case ts.SyntaxKind.ExternalModuleReference: break; case ts.SyntaxKind.JsxElement: break; case ts.SyntaxKind.JsxSelfClosingElement: break; case ts.SyntaxKind.JsxOpeningElement: break; case ts.SyntaxKind.JsxClosingElement: break; case ts.SyntaxKind.JsxFragment: break; case ts.SyntaxKind.JsxOpeningFragment: break; case ts.SyntaxKind.JsxClosingFragment: break; case ts.SyntaxKind.JsxAttribute: break; case ts.SyntaxKind.JsxAttributes: break; case ts.SyntaxKind.JsxSpreadAttribute: break; case ts.SyntaxKind.JsxExpression: break; case ts.SyntaxKind.CaseClause: break; case ts.SyntaxKind.DefaultClause: break; case ts.SyntaxKind.HeritageClause: break; case ts.SyntaxKind.CatchClause: break; case ts.SyntaxKind.PropertyAssignment: break; case ts.SyntaxKind.ShorthandPropertyAssignment: break; case ts.SyntaxKind.SpreadAssignment: break; case ts.SyntaxKind.EnumMember: break; case ts.SyntaxKind.UnparsedPrologue: break; case ts.SyntaxKind.UnparsedPrepend: break; case ts.SyntaxKind.UnparsedText: break; case ts.SyntaxKind.UnparsedInternalText: break; case ts.SyntaxKind.UnparsedSyntheticReference: break; case ts.SyntaxKind.SourceFile: break; case ts.SyntaxKind.Bundle: break; case ts.SyntaxKind.UnparsedSource: break; case ts.SyntaxKind.InputFiles: break; case ts.SyntaxKind.JSDocTypeExpression: break; case ts.SyntaxKind.JSDocAllType: break; case ts.SyntaxKind.JSDocUnknownType: break; case ts.SyntaxKind.JSDocNullableType: break; case ts.SyntaxKind.JSDocNonNullableType: break; case ts.SyntaxKind.JSDocOptionalType: break; case ts.SyntaxKind.JSDocFunctionType: break; case ts.SyntaxKind.JSDocVariadicType: break; case ts.SyntaxKind.JSDocNamepathType: break; case ts.SyntaxKind.JSDocComment: break; case ts.SyntaxKind.JSDocTypeLiteral: break; case ts.SyntaxKind.JSDocSignature: break; case ts.SyntaxKind.JSDocTag: break; case ts.SyntaxKind.JSDocAugmentsTag: break; case ts.SyntaxKind.JSDocImplementsTag: break; case ts.SyntaxKind.JSDocAuthorTag: break; case ts.SyntaxKind.JSDocClassTag: break; case ts.SyntaxKind.JSDocPublicTag: break; case ts.SyntaxKind.JSDocPrivateTag: break; case ts.SyntaxKind.JSDocProtectedTag: break; case ts.SyntaxKind.JSDocReadonlyTag: break; case ts.SyntaxKind.JSDocCallbackTag: break; case ts.SyntaxKind.JSDocEnumTag: break; case ts.SyntaxKind.JSDocParameterTag: break; case ts.SyntaxKind.JSDocReturnTag: break; case ts.SyntaxKind.JSDocThisTag: break; case ts.SyntaxKind.JSDocTypeTag: break; case ts.SyntaxKind.JSDocTemplateTag: break; case ts.SyntaxKind.JSDocTypedefTag: break; case ts.SyntaxKind.JSDocPropertyTag: break; case ts.SyntaxKind.SyntaxList: break; case ts.SyntaxKind.NotEmittedStatement: break; case ts.SyntaxKind.PartiallyEmittedExpression: break; case ts.SyntaxKind.CommaListExpression: break; case ts.SyntaxKind.MergeDeclarationMarker: break; case ts.SyntaxKind.EndOfDeclarationMarker: break; case ts.SyntaxKind.SyntheticReferenceExpression: break; case ts.SyntaxKind.Count: break; case ts.SyntaxKind.FirstAssignment: s = s + ' ' + operatorString(node.kind); break; case ts.SyntaxKind.LastAssignment: break; case ts.SyntaxKind.FirstCompoundAssignment: break; case ts.SyntaxKind.LastCompoundAssignment: break; case ts.SyntaxKind.FirstReservedWord: break; case ts.SyntaxKind.LastReservedWord: break; case ts.SyntaxKind.FirstKeyword: break; case ts.SyntaxKind.LastKeyword: break; case ts.SyntaxKind.FirstFutureReservedWord: break; case ts.SyntaxKind.LastFutureReservedWord: break; case ts.SyntaxKind.FirstTypeNode: break; case ts.SyntaxKind.LastTypeNode: break; case ts.SyntaxKind.FirstPunctuation: break; case ts.SyntaxKind.LastPunctuation: break; case ts.SyntaxKind.FirstToken: break; case ts.SyntaxKind.LastToken: break; case ts.SyntaxKind.FirstTriviaToken: break; case ts.SyntaxKind.LastTriviaToken: break; case ts.SyntaxKind.FirstLiteralToken: break; case ts.SyntaxKind.LastLiteralToken: break; case ts.SyntaxKind.FirstTemplateToken: break; case ts.SyntaxKind.LastTemplateToken: break; case ts.SyntaxKind.FirstBinaryOperator: break; case ts.SyntaxKind.LastBinaryOperator: break; case ts.SyntaxKind.FirstStatement: break; case ts.SyntaxKind.LastStatement: break; case ts.SyntaxKind.FirstNode: break; case ts.SyntaxKind.FirstJSDocNode: break; case ts.SyntaxKind.LastJSDocNode: break; case ts.SyntaxKind.FirstJSDocTagNode: break; case ts.SyntaxKind.LastJSDocTagNode: break; } return s; } function visit(node: ts.Node, prefix: string) { console.log(prefix, nodeToString(node)); ts.forEachChild(node, (node) => visit(node, prefix + ' ')); } function dumpASTNode(sourceFile: ts.SourceFile) { ts.forEachChild(sourceFile, (node) => visit(node, '')); } export function DumpAST(fileNames: string[]) { const options: ts.CompilerOptions = { noEmitOnError: true, noImplicitAny: true, target: ts.ScriptTarget.ES2015, module: ts.ModuleKind.ESNext, allowJs: true, noEmit: true, }; const program = ts.createProgram(fileNames, options); const emitResult = program.emit(); const allDiagnostics = ts .getPreEmitDiagnostics(program) .concat(emitResult.diagnostics); const checker = program.getTypeChecker(); //show_programer(program); allDiagnostics.forEach((diagnostic) => { if (diagnostic.file) { const { line, character } = ts.getLineAndCharacterOfPosition( diagnostic.file, diagnostic.start!, ); const message = ts.flattenDiagnosticMessageText( diagnostic.messageText, '\n', ); console.log( `${diagnostic.file.fileName} (${line + 1},${ character + 1 }): ${message}`, ); return undefined; } else { console.log( ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'), ); } }); for (const sourceFile of program.getSourceFiles()) { if (fileNames.find((f) => f == sourceFile.fileName)) { // Walk the tree to search for classes console.log('==========================================='); dumpASTNode(sourceFile); console.log('==========================================='); } } } function ObjectMemberToString(v: any, objvalues: Set): string { if (typeof v == 'object') { if (objvalues.has(v)) return ''; objvalues.add(v); let s = '{'; for (const k in v) { s = s + `${k}:${ObjectMemberToString(v[k], objvalues)}, `; } s = s + '}'; return s; } else if (typeof v == 'function') { return 'Function'; } return `${v}`; } export function ObjectToString(obj: any): string { return ObjectMemberToString(obj, new Set()); } ================================================ FILE: src/error.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export class SyntaxError extends Error { constructor(message: string) { super('[SyntaxError]\n' + message); } } export class UnimplementError extends Error { constructor(message: string) { super('[UnimplementError]\n' + message); } } export class ValidateError extends Error { constructor(message: string) { super('[ValidateError]\n' + message); } } export class TypeError extends Error { constructor(message: string) { super('[TypeError]\n' + message); } } export class ExpressionError extends Error { constructor(message: string) { super('[ExpressionError]\n' + message); } } export class ScopeError extends Error { constructor(message: string) { super('[ScopeError]\n' + message); } } export class SemanticCheckError extends Error { constructor(message: string) { super('[SemanticCheckError]\n' + message); } } export class StatementError extends Error { constructor(message: string) { super('[StatementError]\n' + message); } } export class CommentError extends Error { constructor(message: string) { super('[CommentError]\n' + message); } } ================================================ FILE: src/expression.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import ts from 'typescript'; import { ParserContext } from './frontend.js'; import { ClassScope, ClosureEnvironment, FunctionScope, Scope, importSearchTypes, } from './scope.js'; import { Variable, Parameter } from './variable.js'; import { getCurScope, addSourceMapLoc, isTypeGeneric, processEscape, processGenericType, calculateTypeArguments, methodSpecialize, } from './utils.js'; import { TSFunction, Type, TypeKind, builtinTypes, TSTypeParameter, TSClass, } from './type.js'; import { Logger } from './log.js'; import { SourceMapLoc } from './backend/binaryen/utils.js'; import { ExpressionError } from './error.js'; import { getConfig } from '../config/config_mgr.js'; type OperatorKind = ts.SyntaxKind; type ExpressionKind = ts.SyntaxKind; export class Expression { private kind: ExpressionKind; private type: Type = new Type(); debugLoc: SourceMapLoc | null = null; public tsNode?: ts.Node; constructor(kind: ExpressionKind) { this.kind = kind; } get expressionKind() { return this.kind; } setExprType(type: Type) { this.type = type; } get exprType(): Type { return this.type; } } export class NullKeywordExpression extends Expression { constructor() { super(ts.SyntaxKind.NullKeyword); } } export class UndefinedKeywordExpression extends Expression { constructor() { super(ts.SyntaxKind.UndefinedKeyword); } } export class NumberLiteralExpression extends Expression { private value: number; constructor(value: number) { super(ts.SyntaxKind.NumericLiteral); this.value = value; } get expressionValue(): number { return this.value; } } export class StringLiteralExpression extends Expression { private value: string; constructor(value: string) { super(ts.SyntaxKind.StringLiteral); this.value = value; } get expressionValue(): string { return this.value; } } export class ObjectLiteralExpression extends Expression { constructor( private fields: IdentifierExpression[], private values: Expression[], ) { super(ts.SyntaxKind.ObjectLiteralExpression); } get objectFields(): IdentifierExpression[] { return this.fields; } get objectValues(): Expression[] { return this.values; } } export class ArrayLiteralExpression extends Expression { constructor(private elements: Expression[]) { super(ts.SyntaxKind.ArrayLiteralExpression); } get arrayValues(): Expression[] { return this.elements; } } export class FalseLiteralExpression extends Expression { constructor() { super(ts.SyntaxKind.FalseKeyword); } } export class TrueLiteralExpression extends Expression { constructor() { super(ts.SyntaxKind.TrueKeyword); } } export class IdentifierExpression extends Expression { private identifier: string; constructor(identifier: string) { super(ts.SyntaxKind.Identifier); this.identifier = identifier; } get identifierName(): string { return this.identifier; } } export class BinaryExpression extends Expression { private operator: OperatorKind; private left: Expression; private right: Expression; constructor(operator: OperatorKind, left: Expression, right: Expression) { super(ts.SyntaxKind.BinaryExpression); this.operator = operator; this.left = left; this.right = right; } get operatorKind(): OperatorKind { return this.operator; } get leftOperand(): Expression { return this.left; } get rightOperand(): Expression { return this.right; } } /** EnumerateKeysExpression is a special expression to * enumerate keys of an object */ export class EnumerateKeysExpression extends Expression { private obj: Expression; constructor(obj: Expression) { super(ts.SyntaxKind.PropertyAccessExpression); this.obj = obj; } get targetObj(): Expression { return this.obj; } } /** if we treat binary expression with comma token as BinaryExpression, * we need to apply special handling for this expression in the semantic tree and backend, and * it maybe generate nested block as well * */ export class CommaExpression extends Expression { private _exprs: Expression[]; constructor(exprs: Expression[]) { super(ts.SyntaxKind.CommaToken); this._exprs = exprs; } get exprs() { return this._exprs; } } export class UnaryExpression extends Expression { private operator: OperatorKind; private _operand: Expression; constructor( kind: ExpressionKind, operator: OperatorKind, operand: Expression, ) { super(kind); this.operator = operator; this._operand = operand; } get operatorKind(): OperatorKind { return this.operator; } get operand(): Expression { return this._operand; } } export class ConditionalExpression extends Expression { constructor( private cond: Expression, private trueExpr: Expression, private falseExpr: Expression, ) { super(ts.SyntaxKind.ConditionalExpression); } get condtion(): Expression { return this.cond; } get whenTrue(): Expression { return this.trueExpr; } get whenFalse(): Expression { return this.falseExpr; } } export class CallExpression extends Expression { private expr: Expression; private args: Expression[]; constructor( expr: Expression, args: Expression[] = new Array(0), private _typeArguments?: Type[], ) { super(ts.SyntaxKind.CallExpression); this.expr = expr; this.args = args; } get callExpr(): Expression { return this.expr; } get callArgs(): Expression[] { return this.args; } get typeArguments(): Type[] | undefined { return this._typeArguments; } } export class SuperExpression extends Expression { private args: Expression[] | undefined; constructor(args?: Expression[]) { super(ts.SyntaxKind.SuperKeyword); this.args = args; } get callArgs(): Expression[] | undefined { return this.args; } } export class PropertyAccessExpression extends Expression { private expr: Expression; private property: Expression; public parent?: Expression; accessSetter = false; constructor(expr: Expression, property: Expression) { super(ts.SyntaxKind.PropertyAccessExpression); this.expr = expr; this.property = property; } get propertyAccessExpr(): Expression { return this.expr; } get propertyExpr(): Expression { return this.property; } } export class NewExpression extends Expression { private expr: Expression; private arguments: Array | undefined; private newArrayLen = 0; private lenExpression: Expression | null = null; constructor( expr: Expression, args?: Array, private _typeArguments?: Type[], ) { super(ts.SyntaxKind.NewExpression); this.expr = expr; this.arguments = args; } get newExpr(): Expression { return this.expr; } setArgs(args: Array) { this.arguments = args; } get newArgs(): Array | undefined { return this.arguments; } setArrayLen(arrayLen: number) { this.newArrayLen = arrayLen; } get arrayLen(): number { return this.newArrayLen; } setLenExpr(len: Expression) { this.lenExpression = len; } get lenExpr(): Expression | null { return this.lenExpression; } setTypeArguments(typeArgs: Type[]) { this._typeArguments = typeArgs; } get typeArguments(): Type[] | undefined { return this._typeArguments; } } export class ParenthesizedExpression extends Expression { private expr: Expression; constructor(expr: Expression) { super(ts.SyntaxKind.ParenthesizedExpression); this.expr = expr; } get parentesizedExpr(): Expression { return this.expr; } } export class ElementAccessExpression extends Expression { private expr: Expression; private argumentExpr: Expression; constructor(expr: Expression, argExpr: Expression) { super(ts.SyntaxKind.ElementAccessExpression); this.expr = expr; this.argumentExpr = argExpr; } get accessExpr(): Expression { return this.expr; } get argExpr(): Expression { return this.argumentExpr; } } export class AsExpression extends Expression { private expr: Expression; constructor(expr: Expression) { super(ts.SyntaxKind.AsExpression); this.expr = expr; } get expression(): Expression { return this.expr; } } export class FunctionExpression extends Expression { private _funcScope: FunctionScope; constructor(func: FunctionScope) { super(ts.SyntaxKind.FunctionExpression); this._funcScope = func; } get funcScope(): FunctionScope { return this._funcScope; } } export class TypeOfExpression extends Expression { constructor(private _expr: Expression) { super(ts.SyntaxKind.TypeOfExpression); } get expr() { return this._expr; } } export class SpreadExpression extends Expression { constructor(private _target: Expression) { super(ts.SyntaxKind.SpreadElement); } get target() { return this._target; } } export class TemplateExpression extends Expression { constructor( private _head: StringLiteralExpression, private _spans: TmplSpanExpression[], ) { super(ts.SyntaxKind.TemplateExpression); } get head() { return this._head; } get spans() { return this._spans; } } export class TmplSpanExpression extends Expression { constructor( private _expr: Expression, private _literal: StringLiteralExpression, // middle or tail expr ) { super(ts.SyntaxKind.TemplateSpan); } get expr() { return this._expr; } get literal() { return this._literal; } } export default class ExpressionProcessor { private typeResolver; private nodeScopeMap; private emitSourceMap; constructor(private parserCtx: ParserContext) { this.typeResolver = this.parserCtx.typeResolver; this.nodeScopeMap = this.parserCtx.nodeScopeMap; this.emitSourceMap = getConfig().sourceMap; } visitNode(node: ts.Node): Expression { const expr = this.visitNodeInternal(node); expr.tsNode = node; return expr; } private visitNodeInternal(node: ts.Node): Expression { let res: Expression | null = null; switch (node.kind) { case ts.SyntaxKind.UndefinedKeyword: { const undefinedExpr = new UndefinedKeywordExpression(); undefinedExpr.setExprType( this.typeResolver.generateNodeType(node), ); return undefinedExpr; } case ts.SyntaxKind.NullKeyword: { res = new NullKeywordExpression(); res.setExprType(this.typeResolver.generateNodeType(node)); break; } case ts.SyntaxKind.NumericLiteral: { res = new NumberLiteralExpression( parseFloat((node).text), ); res.setExprType(this.typeResolver.generateNodeType(node)); break; } case ts.SyntaxKind.StringLiteral: { res = new StringLiteralExpression( processEscape((node).getText()), ); res.setExprType(this.typeResolver.generateNodeType(node)); break; } case ts.SyntaxKind.NoSubstitutionTemplateLiteral: { res = new StringLiteralExpression( (node).getText().slice(1, -1), ); res.setExprType(this.typeResolver.generateNodeType(node)); break; } case ts.SyntaxKind.FalseKeyword: { res = new FalseLiteralExpression(); res.setExprType(this.typeResolver.generateNodeType(node)); break; } case ts.SyntaxKind.TrueKeyword: { res = new TrueLiteralExpression(); res.setExprType(this.typeResolver.generateNodeType(node)); break; } case ts.SyntaxKind.Identifier: { const targetIdentifier = (node).getText(); res = new IdentifierExpression(targetIdentifier); let scope = this.parserCtx.getScopeByNode(node) || null; if (!scope) { throw new ExpressionError( `identifier [${targetIdentifier}] doesn't belong to any scope`, ); } const varReferenceScope = scope!.getNearestFunctionScope(); let variable: Variable | undefined = undefined; let maybeClosureVar = false; let exprType: Type = builtinTypes.get('undefined')!; if (varReferenceScope) { while (scope) { variable = scope.findVariable(targetIdentifier, false); if (variable) { exprType = variable.varType; break; } if (varReferenceScope === scope) { /* Variable not found in current function, it may be a closure var, but we still need to check if it's a global var */ maybeClosureVar = true; } scope = scope.parent; } if (maybeClosureVar) { if (scope && scope.getNearestFunctionScope()) { variable!.setVarIsClosure(); (scope as ClosureEnvironment).hasFreeVar = true; } } } if (exprType.kind == TypeKind.UNDEFINED) { /** in order to avoid there is narrowed type checking scope */ let declNode = node; const symbol = this.parserCtx.typeChecker!.getSymbolAtLocation(node); if (symbol && symbol.valueDeclaration) { declNode = symbol.valueDeclaration; exprType = this.typeResolver.generateNodeType(declNode); } } res.setExprType(exprType); break; } case ts.SyntaxKind.BinaryExpression: { const binaryExprNode = node; const leftExpr = this.visitNode(binaryExprNode.left); const rightExpr = this.visitNode(binaryExprNode.right); let expr: Expression = new BinaryExpression( binaryExprNode.operatorToken.kind, leftExpr, rightExpr, ); if ( binaryExprNode.operatorToken.kind === ts.SyntaxKind.CommaToken ) { let exprs: Expression[] = []; if (leftExpr instanceof CommaExpression) { exprs = exprs.concat(leftExpr.exprs); } else { exprs.push(leftExpr); } if (rightExpr instanceof CommaExpression) { exprs = exprs.concat(rightExpr.exprs); } else { exprs.push(rightExpr); } expr = new CommaExpression(exprs); } expr.setExprType(this.typeResolver.generateNodeType(node)); res = expr; break; } case ts.SyntaxKind.PrefixUnaryExpression: { const prefixExprNode = node; const operand = this.visitNode(prefixExprNode.operand); res = new UnaryExpression( ts.SyntaxKind.PrefixUnaryExpression, prefixExprNode.operator, operand, ); res.setExprType(this.typeResolver.generateNodeType(node)); break; } case ts.SyntaxKind.PostfixUnaryExpression: { const postExprNode = node; const operand = this.visitNode(postExprNode.operand); res = new UnaryExpression( ts.SyntaxKind.PostfixUnaryExpression, postExprNode.operator, operand, ); res.setExprType(this.typeResolver.generateNodeType(node)); break; } case ts.SyntaxKind.ConditionalExpression: { const condExprNode = node; const cond = this.visitNode(condExprNode.condition); const whenTrue = this.visitNode(condExprNode.whenTrue); const whenFalse = this.visitNode(condExprNode.whenFalse); res = new ConditionalExpression(cond, whenTrue, whenFalse); res.setExprType(this.typeResolver.generateNodeType(node)); break; } case ts.SyntaxKind.NonNullExpression: { const nonNullExprNode = node; const expr = this.visitNode(nonNullExprNode.expression); /* For non-null operation (!), just forward the target expression */ res = expr; break; } case ts.SyntaxKind.CallExpression: { const callExprNode = node; let expr = this.visitNode(callExprNode.expression); const args = new Array( callExprNode.arguments.length, ); for (let i = 0; i != args.length; ++i) { args[i] = this.visitNode(callExprNode.arguments[i]); } if ( callExprNode.expression.kind === ts.SyntaxKind.SuperKeyword ) { const newSuperExpression = new SuperExpression(args); res = newSuperExpression; break; } // get the list of specialization types const originalFuncType = expr.exprType as TSFunction; let typeArguments: Type[] = []; if (isTypeGeneric(originalFuncType)) { // explicitly declare specialization type typeArguments // e.g. // function genericFunc (v: T){...} // genericFunc(5); const typeParameters = originalFuncType.isMethod ? originalFuncType.belongedClass!.typeArguments ? originalFuncType.belongedClass!.typeArguments : originalFuncType.typeArguments! : originalFuncType.typeArguments!; if (callExprNode.typeArguments) { typeArguments = callExprNode.typeArguments.map((t) => { return this.typeResolver.generateNodeType(t); }); } // specialize by passing parameters if (typeArguments.length == 0) { // argument type const argTypes = callExprNode.arguments.map((t) => { return this.typeResolver.generateNodeType(t); }); // paramter type const formalParameters = originalFuncType.getParamTypes(); typeArguments = calculateTypeArguments( formalParameters, typeParameters, argTypes, ); } if (typeArguments.length > 0) { const typeNames = new Array(); typeArguments.forEach((v) => { if (v.kind !== TypeKind.TYPE_PARAMETER) { if (v instanceof TSClass) { typeNames.push(v.className); } else { typeNames.push(`${v.kind}`); } } }); const typeSignature = typeNames.length > 0 ? '<' + typeNames.join(',') + '>' : ''; const isUpdateTypeParameters = typeArguments.filter((type) => isTypeGeneric(type)) .length == typeArguments.length; if ( callExprNode.expression.kind === ts.SyntaxKind.Identifier ) { const newFuncType = processGenericType( originalFuncType, typeArguments, typeParameters, this.parserCtx, ); if (!isUpdateTypeParameters) { const newIdentifierName = (expr as IdentifierExpression) .identifierName + typeSignature; expr = new IdentifierExpression( newIdentifierName, ); const specializedType = this.parserCtx.currentScope!.findIdentifier( newIdentifierName, true, importSearchTypes.Type, ); if (specializedType) expr.setExprType(specializedType as Type); } else { expr = new IdentifierExpression( ( expr as IdentifierExpression ).identifierName, ); expr.setExprType(newFuncType); } } else if ( callExprNode.expression.kind === ts.SyntaxKind.PropertyAccessExpression ) { // process class method const propertyAccess = expr as PropertyAccessExpression; if ( propertyAccess.propertyAccessExpr instanceof IdentifierExpression && propertyAccess.propertyAccessExpr .exprType instanceof TSClass ) { const identifierName = ( propertyAccess.propertyAccessExpr as IdentifierExpression ).identifierName; const ownerVariable = this.parserCtx.currentScope!.findIdentifier( identifierName, ) as Variable; const classType = ownerVariable.varType as TSClass; const methodName = ( propertyAccess.propertyExpr as IdentifierExpression ).identifierName; /** * class A { * a: number; * echo(param: T) {...}; * } * const a = new A(); * this class type does not contain 'typeParameters', and newExpression does not contain 'typeArguments'. */ if ( !classType.typeArguments && originalFuncType.typeArguments ) { if ( !isUpdateTypeParameters && originalFuncType.belongedScope ) { const newMethodName = methodName + typeSignature; const newPropertyIdentifier = new IdentifierExpression( newMethodName, ); let res = classType.getMethod(newMethodName); if (!res.method) { const origType = classType.getMethod(methodName); methodSpecialize( origType.method!.type, typeArguments, this.parserCtx, ); res = classType.getMethod( newMethodName, ); } if (res.method) newPropertyIdentifier.setExprType( res.method.type, ); const tsNode = expr.tsNode; expr = new PropertyAccessExpression( ( expr as PropertyAccessExpression ).propertyAccessExpr, newPropertyIdentifier, ); if (res.method) expr.setExprType(res.method.type); } } else { const propertyAccessExpr = new IdentifierExpression( identifierName, ); propertyAccessExpr.setExprType(classType); const propertyType = classType.getMethod( methodName, originalFuncType.funcKind, )!.method!.type; const propertyExpr = new IdentifierExpression(methodName); propertyExpr.setExprType(propertyType); const tsNode = expr.tsNode; expr = new PropertyAccessExpression( propertyAccessExpr, propertyExpr, ); expr.setExprType(propertyType); } } } } } // get the list of specialization types end const callExpr = new CallExpression( expr, args, this.buildTypeArguments(callExprNode.typeArguments), ); if (expr instanceof PropertyAccessExpression) expr.parent = callExpr; callExpr.setExprType(this.typeResolver.generateNodeType(node)); res = callExpr; break; } case ts.SyntaxKind.PropertyAccessExpression: { const propAccessExprNode = node; const expr = this.visitNode(propAccessExprNode.expression); const property = this.visitNode(propAccessExprNode.name); res = new PropertyAccessExpression(expr, property); res.setExprType(this.typeResolver.generateNodeType(node)); break; } case ts.SyntaxKind.ParenthesizedExpression: { const expr = this.visitNode( (node).expression, ); res = new ParenthesizedExpression(expr); res.setExprType(this.typeResolver.generateNodeType(node)); break; } case ts.SyntaxKind.NewExpression: { const newExprNode = node; const expr = this.visitNode(newExprNode.expression); const newExpr = new NewExpression(expr); if (newExprNode.arguments !== undefined) { const args = new Array(); for (const arg of newExprNode.arguments) { args.push(this.visitNode(arg)); } if (args.length > 0) (newExpr as NewExpression).setArgs(args); } if (newExprNode.typeArguments) { newExpr.setTypeArguments( this.buildTypeArguments(newExprNode.typeArguments)!, ); } if (expr.expressionKind === ts.SyntaxKind.Identifier) { if ( (expr).identifierName === 'Array' ) { if (!newExprNode.typeArguments) { if (!this.typeResolver.arrayTypeCheck(node)) { throw new ExpressionError( 'new Array without declare element type', ); } } let isLiteral = false; if (newExprNode.arguments) { /* Check if it's created from a literal */ const argLen = newExprNode.arguments.length; if (argLen > 1) { isLiteral = true; } else if (argLen === 1) { const elem = newExprNode.arguments[0]; const elemExpr = this.visitNode(elem); if ( elemExpr.exprType.kind !== TypeKind.NUMBER && elemExpr.exprType.kind !== TypeKind.WASM_I32 && elemExpr.exprType.kind !== TypeKind.WASM_I64 && elemExpr.exprType.kind !== TypeKind.WASM_F32 && elemExpr.exprType.kind !== TypeKind.WASM_F64 ) { isLiteral = true; } } if (isLiteral) { const elemExprs = newExprNode.arguments.map( (a) => { return this.visitNode(a); }, ); newExpr.setArrayLen(argLen); newExpr.setArgs(elemExprs); } else if (argLen === 1) { newExpr.setLenExpr( this.visitNode(newExprNode.arguments[0]), ); } /* else no arguments */ } else { newExpr.setLenExpr(new NumberLiteralExpression(0)); } newExpr.setExprType( this.typeResolver.generateNodeType(node), ); } else { // handling generic types chain if ( expr.exprType instanceof TSClass && isTypeGeneric(expr.exprType) ) { const genericClassType = expr.exprType; const typeParameters = genericClassType.typeArguments; if (typeParameters) { let typeArguments = new Array(); if (newExpr.newArgs) { // argument type const argTypes: Type[] = []; for (const arg of newExpr.newArgs) { argTypes.push(arg.exprType); } // paramter type const formalParameters = genericClassType.ctorType.getParamTypes(); typeArguments = calculateTypeArguments( formalParameters, typeParameters, argTypes, ); } else if (newExpr.typeArguments) { typeArguments = newExpr.typeArguments; } if (typeArguments.length > 0) { const newClassType = processGenericType( genericClassType, typeArguments, typeParameters, this.parserCtx, ); const newIdentifierExpression = new IdentifierExpression( (newClassType as TSClass).className, ); newIdentifierExpression.setExprType( newClassType, ); const newNewExpr = new NewExpression( newIdentifierExpression, newExpr.newArgs, ); newNewExpr.setExprType(newClassType); res = newNewExpr; break; } } } } } newExpr.setExprType(this.typeResolver.generateNodeType(node)); res = newExpr; break; } case ts.SyntaxKind.ObjectLiteralExpression: { const objLiteralNode = node; const fields = new Array(); const values = new Array(); let propertyAssign: | ts.PropertyAssignment | ts.ShorthandPropertyAssignment | ts.MethodDeclaration; for (const property of objLiteralNode.properties) { if ( ts.isPropertyAssignment(property) || ts.isShorthandPropertyAssignment(property) || ts.isMethodDeclaration(property) ) { propertyAssign = property; } else { throw new ExpressionError( `Unimpl accessing property of kind : ${property.kind}`, ); } fields.push( new IdentifierExpression(propertyAssign.name.getText()), ); const init = ts.isPropertyAssignment(propertyAssign) ? propertyAssign.initializer : propertyAssign; values.push(this.visitNode(init)); } res = new ObjectLiteralExpression(fields, values); res.setExprType(this.typeResolver.generateNodeType(node)); break; } case ts.SyntaxKind.ArrayLiteralExpression: { const arrLiteralNode = node; const elements = new Array(); for (const elem of arrLiteralNode.elements) { elements.push(this.visitNode(elem)); } res = new ArrayLiteralExpression(elements); res.setExprType(this.typeResolver.generateNodeType(node)); if (this.emitSourceMap) { addSourceMapLoc(res, node); } break; } case ts.SyntaxKind.AsExpression: { const asExprNode = node; const expr = this.visitNode(asExprNode.expression); const typeNode = asExprNode.type; res = new AsExpression(expr); res.setExprType(this.typeResolver.generateNodeType(typeNode)); break; } case ts.SyntaxKind.ShorthandPropertyAssignment: { const ShorthandPropertyAssignNode = < ts.ShorthandPropertyAssignment >node; const name = ShorthandPropertyAssignNode.name; res = this.visitNode(name); break; } case ts.SyntaxKind.ElementAccessExpression: { const elementAccessExprNode = node; const expr = this.visitNode(elementAccessExprNode.expression); const argExpr = this.visitNode( elementAccessExprNode.argumentExpression, ); res = new ElementAccessExpression(expr, argExpr); res.setExprType(this.typeResolver.generateNodeType(node)); break; } case ts.SyntaxKind.FunctionDeclaration: case ts.SyntaxKind.FunctionExpression: case ts.SyntaxKind.ArrowFunction: case ts.SyntaxKind.MethodDeclaration: { const funcScope = getCurScope(node, this.nodeScopeMap)!; res = new FunctionExpression(funcScope as FunctionScope); res.setExprType(this.typeResolver.generateNodeType(node)); break; } case ts.SyntaxKind.ThisKeyword: { res = new IdentifierExpression('this'); res.setExprType(this.typeResolver.generateNodeType(node)); break; } case ts.SyntaxKind.TypeOfExpression: { const typeofExpr = node; res = new TypeOfExpression( this.visitNode(typeofExpr.expression), ); res.setExprType(this.typeResolver.generateNodeType(typeofExpr)); break; } case ts.SyntaxKind.SuperKeyword: { res = new SuperExpression(); break; } case ts.SyntaxKind.SpreadElement: { const spreadElement = node as ts.SpreadElement; const target = this.visitNode(spreadElement.expression); res = new SpreadExpression(target); res.setExprType( this.typeResolver.generateNodeType( spreadElement.expression, ), ); break; } case ts.SyntaxKind.TemplateHead: case ts.SyntaxKind.TemplateMiddle: case ts.SyntaxKind.TemplateTail: { const head = node; res = new StringLiteralExpression(head.text); // tsc will infer its type as any res.setExprType(builtinTypes.get('string')!); break; } case ts.SyntaxKind.TemplateExpression: { const tplExpr = node; const head = this.visitNode( tplExpr.head, ) as StringLiteralExpression; const spans = tplExpr.templateSpans.map((span) => { const expr = this.visitNode(span.expression); const literal = this.visitNode(span.literal); return new TmplSpanExpression( expr, literal as StringLiteralExpression, ); }); res = new TemplateExpression(head, spans); res.setExprType(this.typeResolver.generateNodeType(node)); break; } default: Logger.warn( `Encounter un-processed expression, kind: ${node.kind}`, ); return new Expression(ts.SyntaxKind.Unknown); } if (this.emitSourceMap) { addSourceMapLoc(res, node); } return res; } buildTypeArguments( typeArguments?: ts.NodeArray, ): Type[] | undefined { if (!typeArguments) return undefined; const types: Type[] = []; for (const tynode of typeArguments!) { const tstype = this.typeResolver.typechecker!.getTypeFromTypeNode(tynode); types.push(this.typeResolver.tsTypeToType(tstype)); } return types; } specializeExpression( expr: Expression, typeArguments: Type[], typeParameters: TSTypeParameter[], currentFuncScope: Scope, ): Expression { let res = expr; const exprType = expr.exprType; if (typeArguments.length == 0 || typeParameters.length == 0) return res; switch (expr.expressionKind) { case ts.SyntaxKind.Identifier: { const identifierExpression = expr as IdentifierExpression; let identifierName = identifierExpression.identifierName; if (identifierName == 'undefined') { return expr; } if (identifierName == 'this') { const newIdentifierExpression = new IdentifierExpression( identifierName, ); const thisVarType = processGenericType( identifierExpression.exprType, typeArguments, typeParameters, this.parserCtx, ); newIdentifierExpression.setExprType(thisVarType); res = newIdentifierExpression; return res; } const typeArgumentsSignature = new Array(); const ret = currentFuncScope.findIdentifier(identifierName); if (ret) { if (ret instanceof TSClass || ret instanceof ClassScope) { if ( isTypeGeneric(exprType) && (exprType as TSClass).typeArguments ) { const types = (exprType as TSClass).typeArguments!; types.forEach((type) => { const index = typeParameters.findIndex((t) => { return t.name === type.name; }); if (index == -1) { throw new ExpressionError( `${type.name} not found in typeParameters`, ); } if ( typeArguments[index].kind !== TypeKind.TYPE_PARAMETER ) typeArgumentsSignature.push( `${typeArguments[index].kind}`, ); }); } } if (ret instanceof FunctionScope) { if (isTypeGeneric(exprType)) { const types = (exprType as TSFunction) .typeArguments!; types.forEach((type) => { const index = typeParameters.findIndex((t) => { return t.name === type.name; }); if (index == -1) { throw new ExpressionError( `${type.name} not found in typeParameters`, ); } if ( typeArguments[index].kind !== TypeKind.TYPE_PARAMETER ) { if ( typeArguments[index] instanceof TSClass ) { typeArgumentsSignature.push( (typeArguments[index] as TSClass) .className, ); } else { typeArgumentsSignature.push( `${typeArguments[index].kind}`, ); } } }); } } const typeSignature = typeArgumentsSignature.length > 0 ? '<' + typeArgumentsSignature.join(',') + '>' : ''; identifierName = identifierName + typeSignature; } const newIdentifierExpression = new IdentifierExpression( identifierName, ); const newExprType = isTypeGeneric(exprType) ? processGenericType( exprType, typeArguments, typeParameters, this.parserCtx, ) : exprType; newIdentifierExpression.setExprType(newExprType); res = newIdentifierExpression; break; } case ts.SyntaxKind.BinaryExpression: { const binaryBinaryExpression = expr as BinaryExpression; const leftExpr = this.specializeExpression( binaryBinaryExpression.leftOperand, typeArguments, typeParameters, currentFuncScope, ); const rightExpr = this.specializeExpression( binaryBinaryExpression.rightOperand, typeArguments, typeParameters, currentFuncScope, ); const newBinaryExpression = new BinaryExpression( binaryBinaryExpression.operatorKind, leftExpr, rightExpr, ); const newExprType = isTypeGeneric(exprType) ? processGenericType( exprType, typeArguments, typeParameters, this.parserCtx, ) : exprType; newBinaryExpression.setExprType(newExprType); res = newBinaryExpression; break; } case ts.SyntaxKind.PrefixUnaryExpression: { const prefixUnaryExpression = expr as UnaryExpression; const newOperand = this.specializeExpression( prefixUnaryExpression.operand, typeArguments, typeParameters, currentFuncScope, ); const newprefixUnaryExpression = new UnaryExpression( ts.SyntaxKind.PrefixUnaryExpression, prefixUnaryExpression.operatorKind, newOperand, ); const newExprType = isTypeGeneric(exprType) ? processGenericType( exprType, typeArguments, typeParameters, this.parserCtx, ) : exprType; newprefixUnaryExpression.setExprType(newExprType); res = newprefixUnaryExpression; break; } case ts.SyntaxKind.PostfixUnaryExpression: { const postfixUnaryExpression = expr as UnaryExpression; const newOperand = this.specializeExpression( postfixUnaryExpression.operand, typeArguments, typeParameters, currentFuncScope, ); const newUnaryExpression = new UnaryExpression( ts.SyntaxKind.PostfixUnaryExpression, postfixUnaryExpression.operatorKind, newOperand, ); const newExprType = isTypeGeneric(exprType) ? processGenericType( exprType, typeArguments, typeParameters, this.parserCtx, ) : exprType; newUnaryExpression.setExprType(newExprType); res = newUnaryExpression; break; } case ts.SyntaxKind.ConditionalExpression: { const conditionalExpression = expr as ConditionalExpression; const newCondition = this.specializeExpression( conditionalExpression.condtion, typeArguments, typeParameters, currentFuncScope, ); const newTrueExpr = this.specializeExpression( conditionalExpression.whenTrue, typeArguments, typeParameters, currentFuncScope, ); const newFalseExpr = this.specializeExpression( conditionalExpression.whenFalse, typeArguments, typeParameters, currentFuncScope, ); const newConditionalExpression = new ConditionalExpression( newCondition, newTrueExpr, newFalseExpr, ); const newExprType = isTypeGeneric(exprType) ? processGenericType( exprType, typeArguments, typeParameters, this.parserCtx, ) : exprType; newConditionalExpression.setExprType(newExprType); res = newConditionalExpression; break; } case ts.SyntaxKind.CallExpression: { const callExpression = expr as CallExpression; let callExpr = callExpression.callExpr; const args = new Array( callExpression.callArgs.length, ); for (let i = 0; i != args.length; ++i) { args[i] = this.specializeExpression( callExpression.callArgs[i], typeArguments, typeParameters, currentFuncScope, ); } if (callExpr.expressionKind === ts.SyntaxKind.Identifier) { const identifierExpression = callExpr as IdentifierExpression; const exprType = identifierExpression.exprType as TSFunction; if (isTypeGeneric(exprType)) { const typeArguments: Type[] = []; for (let idx = 0; idx < args.length; idx++) { typeArguments.push(args[idx].exprType); } callExpr = this.specializeExpression( identifierExpression, typeArguments, exprType.typeArguments!, currentFuncScope, ); } } else if ( callExpr.expressionKind === ts.SyntaxKind.PropertyAccessExpression ) { const propertyAccessExpression = callExpr as PropertyAccessExpression; callExpr = this.specializeExpression( propertyAccessExpression, typeArguments, typeParameters, currentFuncScope, ); } const newCallExpression = new CallExpression(callExpr, args); if (callExpr instanceof PropertyAccessExpression) callExpr.parent = newCallExpression; const newExprType = isTypeGeneric(exprType) ? processGenericType( exprType, typeArguments, typeParameters, this.parserCtx, ) : exprType; newCallExpression.setExprType(newExprType); res = newCallExpression; break; } case ts.SyntaxKind.PropertyAccessExpression: { const propertyAccessExpression = expr as PropertyAccessExpression; let propertyAccessExpr = propertyAccessExpression.propertyAccessExpr; let propertyExpr = propertyAccessExpression.propertyExpr; if ( propertyAccessExpr.exprType instanceof TSClass && isTypeGeneric(propertyAccessExpr.exprType) ) { const ownerType = propertyAccessExpr.exprType; let propertyName = (propertyExpr as IdentifierExpression) .identifierName; let propertyType: Type = propertyExpr.exprType; propertyAccessExpr = this.specializeExpression( propertyAccessExpr, typeArguments, typeParameters, currentFuncScope, ); // method call if (propertyExpr.exprType instanceof TSFunction) { const funcKind = propertyExpr.exprType.funcKind; if (ownerType.typeArguments) { propertyType = ( propertyAccessExpr.exprType as TSClass ).getMethod(propertyName, funcKind)!.method!.type; } else { const typeArgumentsSignature: Array = []; const _typeParameters = propertyExpr.exprType.typeArguments; if (_typeParameters) { typeArguments.forEach((t) => { if (t.kind !== TypeKind.TYPE_PARAMETER) typeArgumentsSignature.push( `${t.kind}`, ); }); } const typeSignature = typeArgumentsSignature.length > 0 ? '<' + typeArgumentsSignature.join(',') + '>' : ''; propertyName = propertyName + typeSignature; propertyType = ( propertyAccessExpr.exprType as TSClass ).getMethod(propertyName, funcKind)!.method!.type; } } else { // field access //member field (propertyAccessExpr.exprType as TSClass).fields.forEach( (f) => { if (f.name == propertyName) { propertyType = f.type; } }, ); //static field ( propertyAccessExpr.exprType as TSClass ).staticFields.forEach((f) => { if (f.name == propertyName) { propertyType = f.type; } }); } propertyExpr = new IdentifierExpression(propertyName); propertyExpr.setExprType(propertyType); } else { propertyAccessExpr = this.specializeExpression( propertyAccessExpr, typeArguments, typeParameters, currentFuncScope, ); propertyExpr = this.specializeExpression( propertyExpr, typeArguments, typeParameters, currentFuncScope, ); } const newPropertyAccessExpression = new PropertyAccessExpression( propertyAccessExpr, propertyExpr, ); newPropertyAccessExpression.setExprType(propertyExpr.exprType); res = newPropertyAccessExpression; break; } case ts.SyntaxKind.ParenthesizedExpression: { const parenthesizedExpression = expr as ParenthesizedExpression; const newParentesizedExpr = this.specializeExpression( parenthesizedExpression.parentesizedExpr, typeArguments, typeParameters, currentFuncScope, ); const newParenthesizedExpression = new ParenthesizedExpression( newParentesizedExpr, ); const newExprType = isTypeGeneric(exprType) ? processGenericType( exprType, typeArguments, typeParameters, this.parserCtx, ) : exprType; newParenthesizedExpression.setExprType(newExprType); res = newParenthesizedExpression; break; } case ts.SyntaxKind.NewExpression: { const newExpression = expr as NewExpression; if (!newExpression.newArgs && !newExpression.typeArguments) return newExpression; const args: Array = []; if (newExpression.newArgs) { for (let i = 0; i != newExpression.newArgs.length; ++i) { const argExpr = this.specializeExpression( newExpression.newArgs[i], typeArguments, typeParameters, currentFuncScope, ); args.push(argExpr); } } if ( newExpression.newExpr.expressionKind === ts.SyntaxKind.Identifier ) { const identifierExpression = newExpression.newExpr as IdentifierExpression; const newIdentifierExpression = this.specializeExpression( identifierExpression, typeArguments, typeParameters, currentFuncScope, ); res = new NewExpression(newIdentifierExpression, args); res.setExprType(newIdentifierExpression.exprType); } break; } case ts.SyntaxKind.ObjectLiteralExpression: { const objectLiteralExpression = expr as ObjectLiteralExpression; const fields = new Array(); const values = new Array(); for (const f of objectLiteralExpression.objectFields) { fields.push( this.specializeExpression( f, typeArguments, typeParameters, currentFuncScope, ) as IdentifierExpression, ); } for (const v of objectLiteralExpression.objectValues) { values.push( this.specializeExpression( v, typeArguments, typeParameters, currentFuncScope, ), ); } const newObjectLiteralExpression = new ObjectLiteralExpression( fields, values, ); const newExprType = isTypeGeneric(exprType) ? processGenericType( exprType, typeArguments, typeParameters, this.parserCtx, ) : exprType; newObjectLiteralExpression.setExprType(newExprType); res = newObjectLiteralExpression; break; } case ts.SyntaxKind.ArrayLiteralExpression: { const arrayLiteralExpression = expr as ArrayLiteralExpression; const elements = new Array(); for (const elem of arrayLiteralExpression.arrayValues) { elements.push( this.specializeExpression( elem, typeArguments, typeParameters, currentFuncScope, ), ); } const newArrayLiteralExpression = new ArrayLiteralExpression( elements, ); const newExprType = isTypeGeneric(exprType) ? processGenericType( exprType, typeArguments, typeParameters, this.parserCtx, ) : exprType; newArrayLiteralExpression.setExprType(newExprType); res = newArrayLiteralExpression; break; } case ts.SyntaxKind.AsExpression: { const asExpression = expr as AsExpression; const newExpr = this.specializeExpression( asExpression.expression, typeArguments, typeParameters, currentFuncScope, ); const newAsExpression = new AsExpression(newExpr); const newExprType = isTypeGeneric(exprType) ? processGenericType( exprType, typeArguments, typeParameters, this.parserCtx, ) : exprType; newAsExpression.setExprType(newExprType); res = newAsExpression; break; } case ts.SyntaxKind.ElementAccessExpression: { const elementAccessExprNode = expr as ElementAccessExpression; const newAccessExpr = this.specializeExpression( elementAccessExprNode.accessExpr, typeArguments, typeParameters, currentFuncScope, ); const newArgExpr = this.specializeExpression( elementAccessExprNode.argExpr, typeArguments, typeParameters, currentFuncScope, ); const newElementAccessExpression = new ElementAccessExpression( newAccessExpr, newArgExpr, ); const newExprType = isTypeGeneric(exprType) ? processGenericType( exprType, typeArguments, typeParameters, this.parserCtx, ) : exprType; newElementAccessExpression.setExprType(newExprType); res = newElementAccessExpression; break; } case ts.SyntaxKind.FunctionDeclaration: case ts.SyntaxKind.FunctionExpression: case ts.SyntaxKind.ArrowFunction: case ts.SyntaxKind.MethodDeclaration: { const functionExpression = expr as FunctionExpression; const funcScope = functionExpression.funcScope; const newFuncScope = new FunctionScope(currentFuncScope); funcScope.specialize(newFuncScope); // specialize this new FunctionScope newFuncScope.setClassName(funcScope.className); newFuncScope.debugFilePath = funcScope.debugFilePath; newFuncScope.setFuncName(funcScope.funcName); const newFuncType = isTypeGeneric(funcScope.funcType) ? processGenericType( funcScope.funcType, typeArguments, typeParameters, this.parserCtx, ) : funcScope.funcType; newFuncScope.setFuncType(newFuncType as TSFunction); newFuncScope.setGenericOwner(funcScope); funcScope.addSpecializedScope(funcScope.funcName, newFuncScope); newFuncScope.hasFreeVar = funcScope.hasFreeVar; newFuncScope.mangledName = newFuncScope.parent!.mangledName + '|' + newFuncScope.getName(); funcScope.paramArray.forEach((v) => { let varType = v.varType; let initExpression = v.initExpression; if (typeArguments) { varType = processGenericType( v.varType, typeArguments, typeParameters, this.parserCtx, ); initExpression = initExpression ? this.specializeExpression( initExpression, typeArguments, typeParameters, newFuncScope, ) : initExpression; } const new_parameter = new Parameter( v.varName, varType, v.varModifiers, v.varIndex, v.isOptional, v.destructuring, initExpression, v.isLocalVar(), ); newFuncScope.addParameter(new_parameter); }); funcScope.varArray.forEach((v) => { if (v.varName == '@context') { const contextVar = new Variable( '@context', v.varType, v.varModifiers, v.varIndex, v.isLocalVar(), v.initExpression, ); contextVar.scope = newFuncScope; newFuncScope.contextVariable = contextVar; newFuncScope.addVariable(contextVar); } }); funcScope.statements.forEach((s) => { const stmt = this.parserCtx.statementSpecializationProcessor.processStatement( s, typeArguments, typeParameters, currentFuncScope, ); newFuncScope.addStatement(stmt); }); const newFunctionExpression = new FunctionExpression( newFuncScope, ); const newExprType = isTypeGeneric(exprType) ? processGenericType( exprType, typeArguments, typeParameters, this.parserCtx, ) : exprType; newFunctionExpression.setExprType(newExprType); res = newFunctionExpression; break; } case ts.SyntaxKind.SuperKeyword: { const superExpression = expr as SuperExpression; const args: Array = []; if (superExpression.callArgs) { for (let i = 0; i != superExpression.callArgs.length; ++i) { args.push( this.specializeExpression( superExpression.callArgs[i], typeArguments, typeParameters, currentFuncScope, ), ); } } const newSuperExpression = new SuperExpression(args); const newExprType = isTypeGeneric(exprType) ? processGenericType( exprType, typeArguments, typeParameters, this.parserCtx, ) : exprType; newSuperExpression.setExprType(newExprType); res = newSuperExpression; break; } default: res = expr; } res.tsNode = expr.tsNode; return res; } } ================================================ FILE: src/frontend.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import ts from 'typescript'; import { TypeResolver, CustomTypeResolver, TSClass } from './type.js'; import { CustomTypeId, mangling, Stack } from './utils.js'; import { fileURLToPath } from 'url'; import { FunctionScope, GlobalScope, Scope, ScopeKind, ScopeScanner, } from './scope.js'; import { VariableScanner, VariableInit } from './variable.js'; import ExpressionProcessor from './expression.js'; import { StatementProcessor, StatementSpecializationProcessor, } from './statement.js'; import path from 'path'; import { Logger } from './log.js'; import { SyntaxError } from './error.js'; import SemanticChecker from './semantic_check.js'; import { BuiltinNames } from '../lib/builtin/builtin_name.js'; import { ImportResolver } from './import_resolve.js'; export const COMPILER_OPTIONS: ts.CompilerOptions = { module: ts.ModuleKind.ESNext, target: ts.ScriptTarget.ES2015, strict: true, /* disable some features to speedup tsc */ skipLibCheck: true, skipDefaultLibCheck: true, types: [], experimentalDecorators: true, noLib: true, }; export class ParserContext { private _scopeScanner; private _importResolver; private _typeResolver; private _variableScanner; private _variableInit; private _customTypeResolver; private _exprProcessor; private _stmtProcessor; private _stmtSpecializationProcessor; private _sematicChecker; private _errorMessage: ts.Diagnostic[] | null = null; /** These types form a circular reference and need to be created as wasm types using rec. */ private _recTypeGroups = new Array(); typeChecker: ts.TypeChecker | undefined; globalScopes = new Array(); nodeScopeMap = new Map(); currentScope: Scope | null = null; sourceFileLists: ts.SourceFile[] = []; typeId = CustomTypeId; /* mapping type_string to type id */ typeIdMap = new Map(); builtinPath = path.join( path.dirname(fileURLToPath(import.meta.url)), '..', 'lib', 'builtin', ); builtinFileNames = BuiltinNames.builtinFileNames.map((builtinFileName) => { return path.join(this.builtinPath, builtinFileName); }); constructor() { this._scopeScanner = new ScopeScanner(this); this._importResolver = new ImportResolver(this); this._typeResolver = new TypeResolver(this); this._variableScanner = new VariableScanner(this); this._variableInit = new VariableInit(this); this._customTypeResolver = new CustomTypeResolver(this); this._exprProcessor = new ExpressionProcessor(this); this._stmtProcessor = new StatementProcessor(this); this._stmtSpecializationProcessor = new StatementSpecializationProcessor(this); this._sematicChecker = new SemanticChecker(this); } parse(fileNames: string[]): void { const compilerOptions: ts.CompilerOptions = this.getCompilerOptions(); const program: ts.Program = ts.createProgram( [...this.builtinFileNames, ...fileNames], compilerOptions, ); this.typeChecker = program.getTypeChecker(); const allDiagnostics = ts.getPreEmitDiagnostics(program); if (allDiagnostics.length > 0) { const formattedError = ts.formatDiagnosticsWithColorAndContext( allDiagnostics, { getCurrentDirectory: () => { return path.dirname(fileURLToPath(import.meta.url)); }, getCanonicalFileName: (fileNames) => { return fileNames; }, getNewLine: () => { return '\n'; }, }, ); this._errorMessage = allDiagnostics as ts.Diagnostic[]; throw new SyntaxError(formattedError); } const sourceFileList = Array.from(program.getSourceFiles()); this.sourceFileLists = sourceFileList; /* Step1: Resolve all scopes */ this._scopeScanner.visit(sourceFileList); /* Step2: Resolve all type declarations */ this._typeResolver.visitSymbolNode(sourceFileList); this._typeResolver.visit(); /* Step3: Resolve all import and export */ this._importResolver.visit(); /* Step4: Add variables to scopes */ this._variableScanner.visit(); this._variableInit.visit(); /* Step5: Mangling function and global variable name */ mangling(this.globalScopes); /* Step6: Add statements to scopes */ this._stmtProcessor.visit(); this._stmtSpecializationProcessor.visit(); /* Step7: Resolve context type and this type */ this._customTypeResolver.visit(); /* Step8: Additional semantic check */ this._sematicChecker.sematicCheck(); this.dumpScopes(Logger.debug, Logger.debug); if (process.env['TS2WASM_DUMP_SCOPE']) { this.dumpScopes(); } } getScopeByNode(node: ts.Node): Scope | undefined { let res: Scope | undefined; while (node) { res = this.nodeScopeMap.get(node); if (res) { break; } node = node.parent; } return res; } get typeResolver() { return this._typeResolver; } get expressionProcessor(): ExpressionProcessor { return this._exprProcessor; } get statementProcessor(): StatementProcessor { return this._stmtProcessor; } get statementSpecializationProcessor(): StatementSpecializationProcessor { return this._stmtSpecializationProcessor; } get semanticChecker(): SemanticChecker { return this._sematicChecker; } get errorMessage() { return this._errorMessage; } set recGroupTypes(recs: TSClass[][]) { this._recTypeGroups = recs; } get recGroupTypes() { return this._recTypeGroups; } dumpScopes( logFunc: (msg: string) => void = console.log, tableFunc: (msg: any) => void = console.table, ) { const scopes = this.generateScopeInfos(); const scopeInfos: Array = scopes.scopeInfos; const scopeVarInfos: Array = scopes.scopeVarInfos; const scopeTypeInfos: Array = scopes.scopeTypeInfos; for (let i = 0; i < scopeInfos.length; ++i) { logFunc( `============= Variables in scope '${scopeInfos[i].name}' (${scopeInfos[i].kind}) =============`, ); tableFunc(scopeVarInfos[i]); logFunc( `============= Types in scope '${scopeInfos[i].name}' (${scopeInfos[i].kind})=============`, ); tableFunc(scopeTypeInfos[i]); } logFunc(`============= Scope Summary =============`); tableFunc(scopeInfos); } generateScopeInfos() { const scopeInfos: Array = []; const scopeVarInfos: Array = []; const scopeTypeInfos: Array = []; for (let i = 0; i < this.globalScopes.length; ++i) { const scope = this.globalScopes[i]; scope.traverseScopTree((scope) => { const scopeName = scope.getName(); let paramCount = 0; if (scope.kind === ScopeKind.FunctionScope) { const funcScope = scope; paramCount = funcScope.paramArray.length; } scopeInfos.push({ kind: `${scope.kind}`, name: scopeName, param_cnt: paramCount, var_cnt: scope.varArray.length, stmt_cnt: scope.statements.length, child_cnt: scope.children.length, }); const varInfos: Array = []; if (scope.kind === ScopeKind.FunctionScope) { (scope).paramArray.forEach((v) => { let displayName = v.varName; if (displayName === '') { displayName = '@context'; } varInfos.push({ kind: 'param', name: displayName, type: v.varType, isClosure: v.varIsClosure, modifiers: v.varModifiers, index: v.varIndex, }); }); } scope.varArray.forEach((v) => { let displayName = v.varName; if (displayName === '') { displayName = '@context'; } varInfos.push({ kind: 'var', name: displayName, type: v.varType, isClosure: v.varIsClosure, modifiers: v.varModifiers, index: v.varIndex, }); }); scopeVarInfos.push(varInfos); const typeInfos: Array = []; scope.namedTypeMap.forEach((t, name) => { typeInfos.push({ name: name, type: t, }); }); scopeTypeInfos.push(typeInfos); }); } return { scopeInfos, scopeVarInfos, scopeTypeInfos }; } private getCompilerOptions() { const opts: ts.CompilerOptions = {}; for (const i of Object.keys(COMPILER_OPTIONS)) { opts[i] = COMPILER_OPTIONS[i]; } return opts; } } ================================================ FILE: src/import_resolve.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import ts from 'typescript'; import path from 'path'; import { ParserContext } from './frontend.js'; import { convertWindowsPath, generateNodeExpression, getExportIdentifierName, getGlobalScopeByModuleName, getImportIdentifierName, getModulePath, } from './utils.js'; import { GlobalScope, Scope } from './scope.js'; import { Expression, IdentifierExpression } from './expression.js'; export class ImportResolver { globalScopes: Array; currentScope: Scope | null = null; nodeScopeMap: Map; constructor(private parserCtx: ParserContext) { this.globalScopes = this.parserCtx.globalScopes; this.nodeScopeMap = this.parserCtx.nodeScopeMap; } visit() { /* Handle import and export nodes */ this.nodeScopeMap.forEach((scope, node) => { ts.forEachChild(node, this.visitNode.bind(this)); }); /* Auto import the standard library module for every user file */ const builtinScopes = this.globalScopes.filter((scope) => { return !!this.parserCtx.builtinFileNames.find((name) => { // the contents of builtinFileName and debugFilePath of globalScope are both absolute paths return convertWindowsPath(name) === scope.debugFilePath; }); }); for (let i = 0; i < this.globalScopes.length; i++) { const scope = this.globalScopes[i]; if (builtinScopes.indexOf(scope) < 0) { for (const builtinScope of builtinScopes) { for (const builtinIdentifier of builtinScope.declareIdentifierList) { scope.addImportIdentifier( builtinIdentifier, builtinScope, ); } } } } } visitNode(node: ts.Node): void { this.currentScope = this.parserCtx.getScopeByNode(node)!; switch (node.kind) { case ts.SyntaxKind.ImportDeclaration: { const importDeclaration = node; const globalScope = this.currentScope!.getRootGloablScope()!; // Get the import module name according to the relative position of current scope const importModuleName = getModulePath( importDeclaration, this.currentScope!.getRootGloablScope()!, ); const importModuleScope = getGlobalScopeByModuleName( importModuleName!, this.globalScopes, ); // get import identifier const { importIdentifierArray, nameScopeImportName, nameAliasImportMap, defaultImportName, } = getImportIdentifierName(importDeclaration); for (const importIdentifier of importIdentifierArray) { globalScope.addImportIdentifier( importIdentifier, importModuleScope, ); } globalScope.setImportNameAlias(nameAliasImportMap); if (nameScopeImportName) { globalScope.addImportNameScope( nameScopeImportName, importModuleScope, ); } if (defaultImportName) { globalScope.addImportDefaultName( defaultImportName, importModuleScope, ); } if ( !globalScope.importGlobalScopeList.includes( importModuleScope, ) ) { globalScope.importGlobalScopeList.push(importModuleScope); } break; } case ts.SyntaxKind.ExportDeclaration: { const exportDeclaration = node; const globalScope = this.currentScope!.getRootGloablScope()!; const exportModuleName = getModulePath( exportDeclaration, this.currentScope!.getRootGloablScope()!, ); let importModuleScope: GlobalScope | undefined = undefined; if (exportModuleName) { importModuleScope = getGlobalScopeByModuleName( exportModuleName, this.globalScopes, ); } const { nameAliasExportMap, exportIdentifierList } = getExportIdentifierName( exportDeclaration, globalScope, importModuleScope!, ); globalScope.setExportNameAlias(nameAliasExportMap); globalScope.setExportIdentifierList(exportIdentifierList); if (importModuleScope) { const { importIdentifierArray, nameScopeImportName, nameAliasImportMap, } = getImportIdentifierName(exportDeclaration); for (const importIdentifier of importIdentifierArray) { globalScope.addImportIdentifier( importIdentifier, importModuleScope, ); } globalScope.setImportNameAlias(nameAliasImportMap); if (nameScopeImportName) { globalScope.addImportNameScope( nameScopeImportName, importModuleScope, ); } } break; } case ts.SyntaxKind.ExportAssignment: { const exportAssign = node; const globalScope = this.currentScope!.getRootGloablScope()!; let exportExpr: Expression; if (ts.isIdentifier(exportAssign.expression)) { exportExpr = new IdentifierExpression( exportAssign.expression.getText(), ); } else { exportExpr = generateNodeExpression( this.parserCtx.expressionProcessor, exportAssign.expression, ); } globalScope.defaultExpr = exportExpr; globalScope.exportIdentifierList.push(exportExpr); break; } case ts.SyntaxKind.FunctionDeclaration: case ts.SyntaxKind.ClassDeclaration: { const curNode = ( node ); if (ts.getModifiers(curNode)) { for (const modifier of ts.getModifiers(curNode)!) { if (modifier.kind === ts.SyntaxKind.DefaultKeyword) { const globalScope = this.currentScope!.getRootGloablScope()!; const defaultName = curNode.name!.getText()!; globalScope.defaultExpr = new IdentifierExpression( defaultName, ); break; } } } break; } default: { ts.forEachChild(node, this.visitNode.bind(this)); } } } } ================================================ FILE: src/log.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import path from 'path'; import log4js from 'log4js'; import stackTrace from 'stacktrace-js'; import config from '../config/log4js.js'; export enum LoggerLevel { TRACE = 'TRACE', DEBUG = 'DEBUG', INFO = 'INFO', WARN = 'WARN', ERROR = 'ERROR', } log4js.configure(config); const traceLogger = log4js.getLogger(); traceLogger.level = LoggerLevel.TRACE; const productionLogger = log4js.getLogger('production'); productionLogger.level = LoggerLevel.ERROR; export const consoleLogger = log4js.getLogger('console'); consoleLogger.level = LoggerLevel.ERROR; export class Logger { static trace(...args: any[]) { Logger.collectLogs(args, LoggerLevel.TRACE); } static debug(...args: any[]) { Logger.collectLogs(args, LoggerLevel.DEBUG); } static info(...args: any[]) { Logger.collectLogs(args, LoggerLevel.INFO); } static warn(...args: any[]) { Logger.collectLogs(args, LoggerLevel.WARN); } static error(...args: any[]) { Logger.collectLogs(args, LoggerLevel.ERROR); } static getLocation(depth = 3): string { const stackFrames = stackTrace.getSync(); const stackFrame = stackFrames[depth]; const lineNumber = stackFrame.lineNumber!; const columnNumber = stackFrame.columnNumber!; const fileName = stackFrame.fileName!; const pathBaseName = path.basename(fileName); return `${pathBaseName} (line: ${lineNumber}, column: ${columnNumber}): \n`; } static collectLogs(args: any[], logLevel: LoggerLevel) { if (process.env.NODE_ENV === 'production') { if (logLevel === LoggerLevel.ERROR) { productionLogger.error((Logger.getLocation(), args)); } } else { if (logLevel === LoggerLevel.TRACE) { traceLogger.trace(Logger.getLocation(), args); } else if (logLevel === LoggerLevel.DEBUG) { traceLogger.debug(Logger.getLocation(), args); } else if (logLevel === LoggerLevel.INFO) { traceLogger.info(Logger.getLocation(), args); } else if (logLevel === LoggerLevel.WARN) { traceLogger.warn(Logger.getLocation(), args); } else if (logLevel === LoggerLevel.ERROR) { traceLogger.error(Logger.getLocation(), args); } } } } ================================================ FILE: src/scope.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import ts from 'typescript'; import path from 'path'; import { Type, TSFunction, TSClass, builtinTypes, FunctionKind, getMethodPrefix, TSContext, builtinWasmTypes, } from './type.js'; import { ParserContext } from './frontend.js'; import { parentIsFunctionLike, isTypeGeneric, NativeSignature, Import, Export, parseComment, parseCommentBasedFuncNode, } from './utils.js'; import { Parameter, Variable } from './variable.js'; import { Statement } from './statement.js'; import { BuiltinNames } from '../lib/builtin/builtin_name.js'; import { BinaryExpression, Expression, IdentifierExpression, } from './expression.js'; import { Logger } from './log.js'; import { SourceMapLoc } from './backend/binaryen/utils.js'; import { ScopeError } from './error.js'; import { getConfig } from '../config/config_mgr.js'; export enum ScopeKind { Scope, GlobalScope = 'Global', FunctionScope = 'Function', BlockScope = 'Block', ClassScope = 'Class', NamespaceScope = 'Namespace', } export enum importSearchTypes { Variable = 'variable', Type = 'type', Function = 'function', Namespace = 'namespace', Class = 'class', All = 'all', } export class Scope { kind = ScopeKind.Scope; protected name = ''; children: Scope[] = []; parent: Scope | null; /* All types defined in this scope */ namedTypeMap: Map = new Map(); /** it used for source map, so every scope has a file path name field * thus we can mapping to specifed source files */ debugFilePath = ''; /* Hold all temp variables inserted during code generation */ private tempVarArray: Variable[] = []; private variableArray: Variable[] = []; private statementArray: Statement[] = []; private localIndex = -1; public mangledName = ''; private modifiers: ts.Node[] = []; // iff this Scope is specialized scope private _genericOwner?: Scope; // iff this Scope is a generic scope private _specializedScopes?: Map; constructor(parent: Scope | null) { this.parent = parent; if (this.parent !== null) { this.parent.addChild(this); } } /* Common get/set for scope names, derived class may introduce wrapper get/set functions for more explicit semantics */ public getName() { return this.name; } public setName(name: string) { this.name = name; } addStatement(statement: Statement) { this.statementArray.push(statement); } get statements(): Statement[] { return this.statementArray; } addVariable(variableObj: Variable) { this.variableArray.push(variableObj); variableObj.scope = this; } addType(name: string, type: Type) { if (!this.namedTypeMap.has(name)) { this.namedTypeMap.set(name, type); /* For generic types, also add an entry for type without type parameter e.g. Array, we may want to search it by Array */ if (name.indexOf('<') != -1 && isTypeGeneric(type)) { this.namedTypeMap.set(name.split('<')[0], type); } } } resetLocalIndex() { this.localIndex = -1; } allocateLocalIndex() { return this.localIndex++; } assignVariableIndex(scope: Scope) { if (scope instanceof FunctionScope || scope instanceof BlockScope) { scope.varArray.forEach((v) => { v.setVarIndex(this.localIndex++); }); } scope.children.forEach((s) => { if (s instanceof BlockScope) { this.assignVariableIndex(s); } }); } initVariableIndex() { if (this.localIndex !== -1) { throw new ScopeError(`Can't initialize variables multiple times`); } if (this instanceof FunctionScope) { this.localIndex = this.paramArray.length + BuiltinNames.envParamLen; } else if (this instanceof GlobalScope) { this.localIndex = 0; } else { return; } this.assignVariableIndex(this); } initParamIndex() { if (this instanceof FunctionScope) { this.paramArray.forEach((p, index) => { p.setVarIndex(index + BuiltinNames.envParamLen); }); } } addTempVar(variableObj: Variable) { if (this.localIndex === -1) { throw new ScopeError( `Can't add temp variable begore index assigned, add variable instead`, ); } this.tempVarArray.push(variableObj); } getTempVars() { return this.tempVarArray; } get varArray(): Variable[] { return this.variableArray; } addChild(child: Scope) { this.children.push(child); } addModifier(modifier: ts.Node) { this.modifiers.push(modifier); } setGenericOwner(genericOwner: Scope | undefined) { this._genericOwner = genericOwner; } get genericOwner(): Scope | undefined { return this._genericOwner; } addSpecializedScope(typeSignature: string, s: Scope) { if (!this._specializedScopes) this._specializedScopes = new Map(); this._specializedScopes.set(typeSignature, s); } get specializedScopes() { return this._specializedScopes; } protected _nestFindScopeItem( name: string, searchFunc: (scope: Scope) => T | undefined, nested = true, ): T | undefined { let result = searchFunc(this); if (result) { return result; } if (nested) { result = this.parent?._nestFindScopeItem(name, searchFunc, nested); } return result; } private _findInImportScope( scope: GlobalScope, name: string, searchType: importSearchTypes = importSearchTypes.All, ) { let res: Variable | Scope | Type | undefined | Expression; let searchName: string; // judge if name is default name if (scope.defaultModuleImportMap.has(name)) { const defaultModule = scope.defaultModuleImportMap.get(name)!; if (defaultModule.defaultExpr instanceof IdentifierExpression) { res = defaultModule.findIdentifier( defaultModule.defaultExpr.identifierName, true, searchType, ); } else { res = defaultModule.defaultExpr; } } else { if ( scope.identifierModuleImportMap.has(name) || scope.nameAliasImportMap.has(name) ) { const originName = scope.nameAliasImportMap.get(name); searchName = originName ? originName : name; const targetModuleScope = scope.identifierModuleImportMap.get(searchName); if (targetModuleScope) { const targetName = targetModuleScope.nameAliasExportMap.get(searchName); const oriTargetName = targetName ? targetName : searchName; res = targetModuleScope.findIdentifier( oriTargetName, true, searchType, ); } } else { // import * as T from xx searchName = name; res = scope.nameScopeModuleImportMap.get(searchName); } } if (!res && searchType === importSearchTypes.Type) { for (const _importGlobalScope of scope.importGlobalScopeList) { if (_importGlobalScope.namedTypeMap.has(name)) { res = _importGlobalScope.namedTypeMap.get(name); break; } } } return res; } public findVariable( variableName: string, nested = true, ): Variable | undefined { return this._nestFindScopeItem( variableName, (scope) => { let res = scope.variableArray.find((v) => { return v.varName === variableName; }); if (!res && scope instanceof FunctionScope) { res = scope.paramArray.find((v) => { return v.varName === variableName; }); } return res; }, nested, ); } public findFunctionScope( functionName: string, nested = true, ): FunctionScope | undefined { return this._nestFindScopeItem( functionName, (scope) => { return scope.children.find((c) => { return ( c instanceof FunctionScope && (c.funcName === functionName || (c.isDeclare() && c.oriFuncName! === functionName)) ); }) as FunctionScope; }, nested, ); } public findClassScope( className: string, nested = true, ): ClassScope | undefined { return this._nestFindScopeItem( className, (scope) => { return scope.children.find((c) => { return ( c instanceof ClassScope && c.className === className // not mangled ); }) as ClassScope; }, nested, ); } public findNamespaceScope( name: string, nested = true, ): NamespaceScope | undefined { return this._nestFindScopeItem( name, (scope) => { return scope.children.find((s) => { return ( s.kind === ScopeKind.NamespaceScope && s.getName() === name ); }) as NamespaceScope; }, nested, ); } public findType(typeName: string, nested = true): Type | undefined { let res = builtinTypes.get(typeName); if (res) { return res; } res = builtinWasmTypes.get(typeName); if (res) { return res; } return this._nestFindScopeItem( typeName, (scope) => { let res = scope.namedTypeMap.get(typeName); if (res) { return res; } if (scope instanceof GlobalScope) { res = this._findInImportScope( scope, typeName, importSearchTypes.Type, ) as Type | undefined; } return res; }, nested, ); } findIdentifier( name: string, nested = true, searchType: importSearchTypes = importSearchTypes.All, convertName = false, ): Variable | Scope | Type | undefined | Expression { return this._nestFindScopeItem( name, (scope) => { let res: Variable | Scope | Type | undefined | Expression; const matchStep = (type: importSearchTypes) => { return ( searchType === importSearchTypes.All || searchType === type ); }; const oriName = (convertName && scope .getRootGloablScope()! .nameAliasExportMap.get(name)) || name; res = /* Step1: Find variable in current scope */ (matchStep(importSearchTypes.Variable) && scope.findVariable(oriName, false)) || /* Step2: Find function in current scope */ (matchStep(importSearchTypes.Function) && scope.findFunctionScope(oriName, false)) || /* Step3: Find type in current scope */ (matchStep(importSearchTypes.Type) && scope.findType(oriName, false)) || /* Step4: Find namespace */ (matchStep(importSearchTypes.Namespace) && scope.findNamespaceScope(oriName, false)) || /* Step5: Find class in current scope */ (matchStep(importSearchTypes.Class) && scope.findClassScope(oriName, false)) || undefined; if (res) { return res; } /* Step5: Find in other module*/ if (scope instanceof GlobalScope) { res = this._findInImportScope(scope, oriName, searchType); } return res; }, nested, ); } private _getScopeByType(type: ScopeKind): T | null { let currentScope: Scope | null = this; while (currentScope !== null) { if (currentScope.kind === type) { return currentScope; } currentScope = currentScope.parent; } return null; } getNearestFunctionScope() { return this._getScopeByType(ScopeKind.FunctionScope); } getNearestClosureEnvironment() { let currentScope: Scope | null = this; while (currentScope !== null) { if (currentScope instanceof ClosureEnvironment) { return currentScope; } currentScope = currentScope.parent; } return undefined; } getRootGloablScope() { return this._getScopeByType(ScopeKind.GlobalScope); } getRootFunctionScope(): FunctionScope | null { let currentScope: Scope | null = this; const flattenScopes: FunctionScope[] = []; while (currentScope !== null) { if (currentScope instanceof FunctionScope) { flattenScopes.push(currentScope); } currentScope = currentScope.parent; } if (flattenScopes.length === 0) { return null; } else { return flattenScopes[flattenScopes.length - 1]; } } public addDeclareName(name: string) { let scope: Scope | null = this; while (scope !== null) { if (scope.kind == ScopeKind.GlobalScope) { (scope as GlobalScope).declareIdentifierList.add(name); break; } if (scope.kind == ScopeKind.NamespaceScope) { name = scope.getName(); } scope = scope.parent; } } public isDeclare(): boolean { if ( this.modifiers.find((modifier) => { return modifier.kind === ts.SyntaxKind.DeclareKeyword; }) ) { return true; } return this.parent?.isDeclare() || false; } public isAbstract(): boolean { if ( this.modifiers.find((modifier) => { return modifier.kind === ts.SyntaxKind.AbstractKeyword; }) ) { return true; } return this.parent?.isDeclare() || false; } public isDefault(): boolean { return !!this.modifiers.find((modifier) => { return modifier.kind === ts.SyntaxKind.DefaultKeyword; }); } public isExport(): boolean { return ( !!this.modifiers.find((modifier) => { return modifier.kind === ts.SyntaxKind.ExportKeyword; }) || this.getRootGloablScope()!.exportIdentifierList.some( (exportExpr) => { return ( exportExpr instanceof IdentifierExpression && exportExpr.identifierName === this.name ); }, ) ); } public isStatic(): boolean { return !!this.modifiers.find((modifier) => { return modifier.kind === ts.SyntaxKind.StaticKeyword; }); } public isDecorator(): boolean { return !!this.modifiers.find( (modifier) => modifier.kind === ts.SyntaxKind.Decorator && (modifier).expression.getText() === 'binaryen', ); } hasDecorator(name: string): boolean { return !!this.modifiers.find( (modifier) => modifier.kind === ts.SyntaxKind.Decorator && (modifier).expression.getText() === name, ); } traverseScopTree(traverseMethod: (scope: Scope) => void) { traverseMethod(this); for (const child of this.children) { child.traverseScopTree(traverseMethod); } } // process generic specialization specialize(scope: Scope) { scope.kind = this.kind; scope.name = this.name; scope.children = new Array(); scope.namedTypeMap = new Map(); scope.debugFilePath = this.debugFilePath; scope.tempVarArray = new Array(); scope.variableArray = new Array(); scope.statementArray = new Array(); scope.localIndex = this.localIndex; scope.mangledName = this.mangledName; scope.modifiers = this.modifiers; } } export class ClosureEnvironment extends Scope { hasFreeVar = false; contextVariable: Variable | undefined = undefined; constructor(parent: Scope | null = null) { super(parent); if ( this instanceof FunctionScope || parent?.getNearestFunctionScope() ) { /* Add 'context' variable if this scope is inside a function scope */ const contextVar = new Variable('@context', new TSContext()); this.addVariable(contextVar); this.contextVariable = contextVar; } } specialize(scope: ClosureEnvironment) { super.specialize(scope); scope.kind = this.kind; scope.hasFreeVar = this.hasFreeVar; } } export class GlobalScope extends Scope { kind = ScopeKind.GlobalScope; private functionName = ''; private functionType = new TSFunction(); // import {xx} from yy, import zz from yy; store [xx, zz] identifierModuleImportMap = new Map(); // import * as T from yy, Scope is T nameScopeModuleImportMap = new Map(); // import {xx as zz} from yy, Alias is zz, store nameAliasImportMap = new Map(); // export alias, export { c as renamed_c }; store nameAliasExportMap = new Map(); exportIdentifierList: Expression[] = []; // default identifier map: import theDefault from "./export-case1"; import theOtherDefault from "./export-case2"; defaultModuleImportMap = new Map(); defaultExpr: Expression | undefined = undefined; node: ts.Node | null = null; debugLocations: SourceMapLoc[] = []; // declare list name declareIdentifierList = new Set(); isCircularImport = false; importStartFuncNameList: string[] = []; importGlobalScopeList: GlobalScope[] = []; constructor(parent: Scope | null = null) { super(parent); } addVariable(variableObj: Variable) { super.addVariable(variableObj); variableObj.setIsLocalVar(false); } set moduleName(moduleName: string) { this.name = moduleName; } get moduleName(): string { return this.name; } get startFuncName(): string { return this.functionName; } set startFuncName(name: string) { this.functionName = name; } get startFuncType(): TSFunction { return this.functionType; } addImportIdentifier(identifier: string, moduleScope: GlobalScope) { if (this.identifierModuleImportMap.has(identifier)) { Logger.warn(`WAMRNING identifier '${identifier}' has been added`); return; } this.identifierModuleImportMap.set(identifier, moduleScope); } addImportDefaultName(defaultImportName: string, moduleScope: GlobalScope) { this.defaultModuleImportMap.set(defaultImportName, moduleScope); } addImportNameScope(nameScope: string, moduleScope: GlobalScope) { this.nameScopeModuleImportMap.set(nameScope, moduleScope); } setImportNameAlias(nameAliasImportMap: Map) { for (const [key, value] of nameAliasImportMap) { this.nameAliasImportMap.set(key, value); } } setExportNameAlias(nameAliasExportMap: Map) { for (const [key, value] of nameAliasExportMap) { this.nameAliasExportMap.set(key, value); } } setExportIdentifierList(exportIdentifierList: Expression[]) { this.exportIdentifierList.push(...exportIdentifierList); } } export class FunctionScope extends ClosureEnvironment { kind = ScopeKind.FunctionScope; private parameterArray: Parameter[] = []; private functionType = new TSFunction(); /* iff the function is a member function, which class it belong to */ private _className = ''; realParamCtxType = new TSContext(); /* ori func name iff func is declare */ oriFuncName: string | undefined = undefined; debugLocations: SourceMapLoc[] = []; comments: (NativeSignature | Import | Export)[] = []; constructor(parent: Scope) { super(parent); this.debugFilePath = parent.debugFilePath; } getThisIndex() { return this.varArray.find((v) => { return v.varName === 'this'; })!.varIndex; } addParameter(parameter: Parameter) { this.parameterArray.push(parameter); parameter.scope = this; } get paramArray(): Parameter[] { return this.parameterArray; } setFuncName(name: string) { this.name = name; } get funcName(): string { return this.name; } get isAnonymose(): boolean { return this.name == '' || this.name.indexOf('@anonymous') == 0; } setFuncType(type: TSFunction) { this.functionType = type; } get funcType(): TSFunction { return this.functionType; } setClassName(name: string) { this._className = name; } get className(): string { return this._className; } isMethod(): boolean { return this._className !== ''; } specialize(funcScope: FunctionScope) { super.specialize(funcScope); funcScope.kind = this.kind; funcScope.parameterArray = new Array(); funcScope.functionType = this.functionType; funcScope._className = this._className; funcScope.realParamCtxType = this.realParamCtxType; funcScope.mangledName = this.mangledName; funcScope.oriFuncName = this.oriFuncName; funcScope.debugLocations = new Array(); } } export class BlockScope extends ClosureEnvironment { kind = ScopeKind.BlockScope; /* belong function scope of this block scope, may be null if this block is in global scope */ funcScope: FunctionScope | null = null; constructor( parent: Scope, name = '', funcScope: FunctionScope | null = null, ) { super(parent); this.name = name; this.funcScope = funcScope; this.debugFilePath = parent.debugFilePath; } } export class ClassScope extends Scope { kind = ScopeKind.ClassScope; private _classType: TSClass = new TSClass(); constructor(parent: Scope, name = '') { super(parent); this.name = name; this.debugFilePath = parent.debugFilePath; } get className(): string { return this.name; } setClassType(type: TSClass) { this._classType = type; } get classType(): TSClass { return this._classType; } specialize(classScope: ClassScope) { super.specialize(classScope); classScope.kind = this.kind; classScope.name = this.name; classScope._classType = this._classType; } } export class NamespaceScope extends Scope { kind = ScopeKind.NamespaceScope; constructor(parent: Scope) { super(parent); this.debugFilePath = parent.debugFilePath; } addVariable(variableObj: Variable) { super.addVariable(variableObj); variableObj.setIsLocalVar(false); } } export class ScopeScanner { globalScopes: Array; currentScope: Scope | null = null; nodeScopeMap: Map; /* anonymous function index */ anonymousIndex = 0; /* block index to represent current block's count */ blockIndex = 0; static literal_obj_count = 0; static specializedScopeCache = new Map[]>(); constructor(private parserCtx: ParserContext) { this.globalScopes = this.parserCtx.globalScopes; this.nodeScopeMap = this.parserCtx.nodeScopeMap; } _generateClassFuncScope( node: ts.FunctionLikeDeclaration, methodKind: FunctionKind, needToAddThisVar = true, ) { const parentScope = this.currentScope!; const functionScope = new FunctionScope(parentScope); parseCommentBasedFuncNode(node, functionScope); if (node.modifiers !== undefined) { for (const modifier of node.modifiers) { functionScope.addModifier(modifier); } } if (methodKind !== FunctionKind.STATIC && needToAddThisVar) { /* record '@this' as env param, add 'this' to varArray */ const thisVar = new Variable('this', new Type()); thisVar.setVarIsClosure(); functionScope.addVariable(thisVar); } functionScope.setClassName((parentScope).className); let methodName = getMethodPrefix(methodKind); if (node.name) { methodName += node.name.getText(); } if (!node.name && ts.isPropertyAssignment(node.parent)) { methodName += node.parent.name.getText(); } functionScope.setFuncName(methodName); this.nodeScopeMap.set(node, functionScope); if ( !functionScope.isDeclare() && !functionScope.isAbstract() && node.body ) { this.setCurrentScope(functionScope); this.visitNode(node.body!); this.setCurrentScope(parentScope); } } visit(nodes: Array) { for (const sourceFile of nodes) { this.visitNode(sourceFile); } } visitNode(node: ts.Node): void { switch (node.kind) { case ts.SyntaxKind.SourceFile: { const sourceFileNode = node; const globalScope = new GlobalScope(); globalScope.node = node; globalScope.debugFilePath = sourceFileNode.fileName; this.setCurrentScope(globalScope); let moduleName = ''; const isBuiltInFile = sourceFileNode.fileName.includes( BuiltinNames.builtinImplementFileName, ); if (isBuiltInFile) { /* Use fixed name for builtin libraries, since currently we use the moduleName as the import module name when generating import entry for WebAssembly */ moduleName = BuiltinNames.builtinModuleName; } else { const filePath = sourceFileNode.fileName.slice( undefined, -'.ts'.length, ); moduleName = path.relative(process.cwd(), filePath); } globalScope.moduleName = moduleName; this.globalScopes.push(globalScope); this.nodeScopeMap.set(sourceFileNode, globalScope); for (let i = 0; i < sourceFileNode.statements.length; i++) { this.visitNode(sourceFileNode.statements[i]); } this.visitNode(sourceFileNode.endOfFileToken); break; } case ts.SyntaxKind.ModuleDeclaration: { const moduleDeclaration = node; const namespaceName = moduleDeclaration.name.text; const parentScope = this.currentScope!; if ( parentScope.kind !== ScopeKind.GlobalScope && parentScope.kind !== ScopeKind.NamespaceScope ) { throw new ScopeError( 'A namespace declaration is only allowed at the top level of a namespace or module', ); } const namespaceScope = new NamespaceScope(parentScope); namespaceScope.setName(namespaceName); if (moduleDeclaration.modifiers !== undefined) { for (const modifier of moduleDeclaration.modifiers) { namespaceScope.addModifier(modifier); } } const moduleBlock = moduleDeclaration.body!; this.setCurrentScope(namespaceScope); this.nodeScopeMap.set(moduleBlock, namespaceScope); const statements = moduleBlock.statements; if (statements.length !== 0) { for (let i = 0; i < statements.length; i++) { this.visitNode(statements[i]); } } this.setCurrentScope(parentScope); break; } case ts.SyntaxKind.FunctionDeclaration: { const funcDecl = node; this._generateFuncScope(funcDecl); break; } case ts.SyntaxKind.FunctionExpression: case ts.SyntaxKind.ArrowFunction: { const funcNode = node as ts.FunctionLikeDeclaration; if (ts.isPropertyAssignment(node.parent)) { let needToAddThisVar = true; if (ts.isArrowFunction(node)) { needToAddThisVar = false; } this._generateClassFuncScope( funcNode, FunctionKind.METHOD, needToAddThisVar, ); } else if (ts.isPropertyDeclaration(node.parent)) { this._generateClassFuncScope(funcNode, FunctionKind.METHOD); } else { this._generateFuncScope(funcNode); } break; } case ts.SyntaxKind.ClassDeclaration: { const classDeclarationNode = node; const parentScope = this.currentScope!; const className = (( classDeclarationNode.name )).getText(); const classScope = new ClassScope(parentScope, className); if (classDeclarationNode.modifiers) { for (const modifier of classDeclarationNode.modifiers) { classScope.addModifier(modifier); } } this.setCurrentScope(classScope); this.nodeScopeMap.set(classDeclarationNode, classScope); for (const member of classDeclarationNode.members) { if ( member.kind === ts.SyntaxKind.SetAccessor || member.kind === ts.SyntaxKind.GetAccessor || member.kind === ts.SyntaxKind.Constructor || member.kind === ts.SyntaxKind.MethodDeclaration ) { this.visitNode(member); } if ( ts.isPropertyDeclaration(member) && member.initializer ) { this.visitNode(member.initializer); } } if (classScope.isDeclare()) { parentScope.addDeclareName(className); } this.setCurrentScope(parentScope); break; } case ts.SyntaxKind.ObjectLiteralExpression: { const objectLiteralNode = node; const parentScope = this.currentScope!; const className = 'literal_obj' + ScopeScanner.literal_obj_count++; const objLiteralScope = new ClassScope(parentScope, className); this.setCurrentScope(objLiteralScope); this.nodeScopeMap.set(objectLiteralNode, objLiteralScope); for (const property of objectLiteralNode.properties) { if (ts.isPropertyAssignment(property)) { this.visitNode(property.initializer); } else { this.visitNode(property); } } this.setCurrentScope(parentScope); break; } case ts.SyntaxKind.InterfaceDeclaration: { const parentScope = this.currentScope!; const interfaceDeclarationNode = node; if (!interfaceDeclarationNode.modifiers) break; const hasDeclareKeyword = interfaceDeclarationNode.modifiers!.find((modifier) => { return modifier.kind == ts.SyntaxKind.DeclareKeyword; }); if (hasDeclareKeyword) { const className = (( interfaceDeclarationNode.name )).getText(); parentScope.addDeclareName(className); } break; } case ts.SyntaxKind.SetAccessor: { this._generateClassFuncScope( node, FunctionKind.SETTER, ); break; } case ts.SyntaxKind.GetAccessor: { this._generateClassFuncScope( node, FunctionKind.GETTER, ); break; } case ts.SyntaxKind.Constructor: { this._generateClassFuncScope( node, FunctionKind.CONSTRUCTOR, ); break; } case ts.SyntaxKind.MethodDeclaration: { const methodNode = node; const kind = methodNode.modifiers?.find((m) => { return m.kind === ts.SyntaxKind.StaticKeyword; }) ? FunctionKind.STATIC : FunctionKind.METHOD; this._generateClassFuncScope(methodNode, kind); break; } case ts.SyntaxKind.Block: { const blockNode = node; this.createBlockScope(blockNode); break; } case ts.SyntaxKind.ForStatement: { const forStatementNode = node; this.createLoopBlockScope(forStatementNode); break; } case ts.SyntaxKind.ForOfStatement: { const forOfStmtNode = node; this.createLoopBlockScope(forOfStmtNode); break; } case ts.SyntaxKind.ForInStatement: { const forInStmtNode = node; this.createLoopBlockScope(forInStmtNode); break; } case ts.SyntaxKind.WhileStatement: { const whileStatementNode = node; this.createLoopBlockScope(whileStatementNode); break; } case ts.SyntaxKind.DoStatement: { const doStatementNode = node; this.createLoopBlockScope(doStatementNode); break; } case ts.SyntaxKind.CaseClause: { const caseClauseNode = node; this.createBlockScope(caseClauseNode); break; } case ts.SyntaxKind.DefaultClause: { const defaultClauseNode = node; this.createBlockScope(defaultClauseNode); break; } case ts.SyntaxKind.VariableDeclaration: { const variableDeclarationNode = node; const currentScope = this.currentScope!; const stmtNode = variableDeclarationNode.parent.parent; if (ts.isVariableStatement(stmtNode) && stmtNode.modifiers) { const hasDeclareKeyword = stmtNode.modifiers!.find( (modifier) => { return ( modifier.kind == ts.SyntaxKind.DeclareKeyword ); }, ); if (hasDeclareKeyword) { const variableName = variableDeclarationNode.name.getText(); currentScope.addDeclareName(variableName); break; } } ts.forEachChild(node, this.visitNode.bind(this)); break; } default: { ts.forEachChild(node, this.visitNode.bind(this)); } } } setCurrentScope(currentScope: Scope | null) { this.currentScope = currentScope; } createBlockScope(node: ts.BlockLike) { const parentScope = this.currentScope!; if (!parentIsFunctionLike(node)) { const parentScope = this.currentScope!; const parentName = parentScope.getName(); const blockName = ts.isCaseOrDefaultClause(node) ? 'case' : 'block'; const maybeFuncScope = parentScope.getNearestFunctionScope(); const blockScope = new BlockScope( parentScope, `${parentName}.${blockName}.${this.blockIndex++}`, maybeFuncScope, ); this.setCurrentScope(blockScope); this.nodeScopeMap.set(node, blockScope); } const statements = node.statements; if (statements.length !== 0) { for (let i = 0; i < statements.length; i++) { this.visitNode(statements[i]); } } if (!parentIsFunctionLike(node)) { this.setCurrentScope(parentScope); } } createLoopBlockScope(node: ts.IterationStatement) { const parentScope = this.currentScope!; const parentName = parentScope.getName(); const maybeFuncScope = parentScope.getNearestFunctionScope(); const outOfLoopBlock = new BlockScope( parentScope, `${parentName}.loop.${this.blockIndex++}`, maybeFuncScope, ); this.setCurrentScope(outOfLoopBlock); this.nodeScopeMap.set(node, outOfLoopBlock); this.visitNode(node.statement); this.setCurrentScope(parentScope); } private _generateFuncScope(node: ts.FunctionLikeDeclaration) { const parentScope = this.currentScope!; const functionScope = new FunctionScope(parentScope); parseCommentBasedFuncNode(node, functionScope); if (node.modifiers !== undefined) { for (const modifier of node.modifiers) { functionScope.addModifier(modifier); } } let functionName: string; if (node.name !== undefined) { functionName = node.name.getText(); } else { functionName = '@anonymous' + this.anonymousIndex++; } /* function context struct placeholder */ this.nodeScopeMap.set(node, functionScope); if (functionScope.isDeclare()) { functionScope.oriFuncName = functionName; functionScope.setFuncName(functionName); // TODO: this may be mistake since scope funcName has changed parentScope.addDeclareName(functionName); } else { functionScope.setFuncName(functionName); this.setCurrentScope(functionScope); this.visitNode(node.body!); this.setCurrentScope(parentScope); } } } ================================================ FILE: src/semantic_check.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import ts from 'typescript'; import { Scope, FunctionScope, GlobalScope, BlockScope, ClassScope, NamespaceScope, } from './scope.js'; import { TSClass, TSInterface, Type, TypeKind, TSArray, TSFunction, } from './type.js'; import { Logger } from './log.js'; import { ParserContext } from './frontend.js'; import { BinaryExpression, CallExpression, Expression, PropertyAccessExpression, } from './expression.js'; import { Parameter, Variable } from './variable.js'; import { BaseLoopStatement, CaseClause, ExpressionStatement, ForStatement, IfStatement, ReturnStatement, Statement, SwitchStatement, } from './statement.js'; import { SemanticCheckError } from './error.js'; const enum ErrorKind { NominalClass = 'nominal class', ClosureOrInnerFunctionDefaultParam = 'closure or inner function with default parameters', InvokeAnyObj = 'invoke any object', VoidTypeAsVarType = 'void type as variable type', } const enum ErrorFlag { BinaryOperationOnNominalClass, ReturnTypesAreNominalClass, ArgsAndParamsTypesAreNominalClass, ClosureOrInnerFuncHasDefaultParams, InvokeAnyObject, VoidTypeAsVarType, } interface SematicError { errorKind: ErrorKind; errorFlag: ErrorFlag; message: string; scopeName: string; } export default class SemanticChecker { private errors: SematicError[] = []; private curScope: Scope | null = null; private globalScopes; constructor(private parserCtx: ParserContext) { this.globalScopes = parserCtx.globalScopes; } sematicCheck() { for (let i = 0; i < this.globalScopes.length; ++i) { const scope = this.globalScopes[i]; scope.traverseScopTree((scope) => { this.curScope = scope; scope.varArray.map((v) => { this.exprAccept(v.initExpression); this.varInitAccept(v); }); if (scope instanceof FunctionScope) { let hasDefaultParam = false; scope.paramArray.map((p) => { const init = p.initExpression; if (init) { hasDefaultParam = true; } this.voidTypeCheck(p.varType.kind); this.exprAccept(init); this.varInitAccept(p); }); if (hasDefaultParam) { this.defaultParamAccept(); } } scope.statements.map((stmt) => { this.stmtAccept(stmt); }); this.curScope = scope.parent; }); } if (this.errors.length > 0) { Logger.error(this.logErrors()); throw new SemanticCheckError('Semantic check error.'); } } private stmtAccept(stmt: Statement) { let expr: Expression | null = null; if (stmt instanceof IfStatement) { expr = stmt.ifCondition; } else if (stmt instanceof ReturnStatement) { expr = stmt.returnExpression; } else if (stmt instanceof BaseLoopStatement) { expr = stmt.loopCondtion; } else if (stmt instanceof ForStatement) { expr = stmt.forLoopCondtion; } else if (stmt instanceof ExpressionStatement) { expr = stmt.expression; } else if (stmt instanceof CaseClause) { expr = stmt.caseExpr; } else if (stmt instanceof SwitchStatement) { expr = stmt.switchCondition; } this.exprAccept(expr); if (stmt instanceof ForStatement) { this.exprAccept(stmt.forLoopIncrementor); } if (stmt instanceof ReturnStatement) { if (stmt.returnExpression) { this.returnTypeCheck(stmt.returnExpression); } } } private exprAccept(expr: Expression | null) { if (expr instanceof CallExpression) { if ( expr.callExpr instanceof PropertyAccessExpression && expr.callExpr.exprType.kind === TypeKind.ANY ) { return; } this.checkCallExpr(expr); } else if (expr instanceof BinaryExpression) { this.binaryOperateCheck( expr.leftOperand.exprType, expr.rightOperand.exprType, ); if (expr.rightOperand instanceof PropertyAccessExpression) { this.invokeAnyObjCheck( expr.leftOperand.exprType, expr.rightOperand.propertyExpr.exprType, ); } } } private varInitAccept(expr: Variable | Parameter) { if (expr.initExpression) { this.voidTypeCheck(expr.varType.kind); this.binaryOperateCheck(expr.varType, expr.initExpression.exprType); if (expr.initExpression instanceof PropertyAccessExpression) { this.invokeAnyObjCheck( expr.varType, expr.initExpression.propertyAccessExpr.exprType, ); } } } // check arguments and parameters types private checkCallExpr(expr: CallExpression) { const calleeType = expr.callExpr.exprType; if (calleeType.kind === TypeKind.FUNCTION) { const funcType = expr.callExpr.exprType as TSFunction; let paramTypes = funcType.getParamTypes(); if (funcType.hasRest()) { const restType = (paramTypes[paramTypes.length - 1]) .elementType; if (paramTypes.length <= expr.callArgs.length) { const restTypes = new Array( expr.callArgs.length - paramTypes.length + 1, ).fill(restType); paramTypes = paramTypes .slice(0, paramTypes.length - 1) .concat(restTypes); } } for (let i = 0; i < expr.callArgs.length; i++) { const paramType = paramTypes[i]; const argExpr = expr.callArgs[i]; this.nominalClassCheck( paramType, argExpr.exprType, ErrorFlag.ArgsAndParamsTypesAreNominalClass, `argument type and parameter type are nominal class types`, ); if (!argExpr.exprType) { console.log('hh'); } this.voidTypeCheck(argExpr.exprType.kind); if (argExpr instanceof PropertyAccessExpression) { this.invokeAnyObjCheck( paramType, argExpr.propertyAccessExpr.exprType, ); } } } else if (calleeType.kind === TypeKind.ANY) { Logger.info('callee expr is any type'); } else if (calleeType.kind === TypeKind.UNION) { Logger.info('callee expr is union type'); } } // check return statement type and function return type private returnTypeCheck(expr: Expression) { const funcScope = this.curScope!.getNearestFunctionScope() as FunctionScope; const returnType = funcScope.funcType.returnType; this.nominalClassCheck( returnType, expr.exprType, ErrorFlag.ReturnTypesAreNominalClass, `return statement type and function return type are nominal classes`, ); if (expr instanceof PropertyAccessExpression) { this.invokeAnyObjCheck( returnType, expr.propertyAccessExpr.exprType, ); } } private binaryOperateCheck(left: Type, right: Type) { this.nominalClassCheck( left, right, ErrorFlag.BinaryOperationOnNominalClass, `binary operation between different nominal classes`, ); } private defaultParamAccept() { const parent = this.curScope!.parent; const parentLevelFuncScope = parent!.getNearestFunctionScope(); if (parentLevelFuncScope) { this.errors.push({ errorKind: ErrorKind.ClosureOrInnerFunctionDefaultParam, errorFlag: ErrorFlag.ClosureOrInnerFuncHasDefaultParams, message: `inner function has default parameters`, scopeName: this.getScopeName(this.curScope!), }); } } /** addtional sematic checking rules */ private nominalClassCheck( left: Type, right: Type, flag: ErrorFlag, msg: string, ) { if (left instanceof TSInterface || right instanceof TSInterface) { return; } if (!(left instanceof TSClass) || !(right instanceof TSClass)) { return; } /** for object literal, compare the two's type ids */ if (left.isLiteral || right.isLiteral) { if (left.typeId === right.typeId) { return; } } const leftName = left.className, rightName = right.className; if (leftName === rightName) { return; } // downcast let base = right.getBase(); while (base) { if (base.className === left.className) { return; } base = base.getBase(); } this.errors.push({ errorKind: ErrorKind.NominalClass, errorFlag: flag, message: msg + ` object(${right.className}[${right.typeId}]) is not able to assgn to object(${left.className}[${left.typeId}])`, scopeName: this.getScopeName(this.curScope!), }); } private invokeAnyObjCheck(left: Type, exprType: Type) { if (left.kind !== TypeKind.ANY && exprType.kind === TypeKind.ANY) { this.errors.push({ errorKind: ErrorKind.InvokeAnyObj, errorFlag: ErrorFlag.InvokeAnyObject, message: `invoke any object without cast to a specific type`, scopeName: this.getScopeName(this.curScope!), }); } } private voidTypeCheck(typeKind: TypeKind) { if (typeKind === TypeKind.VOID) { this.errors.push({ errorKind: ErrorKind.VoidTypeAsVarType, errorFlag: ErrorFlag.VoidTypeAsVarType, message: `Does not allow void type value as variable, or as function argument`, scopeName: this.getScopeName(this.curScope!), }); } } private logErrors() { let res = 'sematic checking not passing: \n'; for (const error of this.errors) { res += `[${error.errorKind}]: in [${error.scopeName}], error flag: '${error.errorFlag}', message: '${error.message}' \n`; } return res; } private getScopeName(scope: Scope): string { if (scope instanceof FunctionScope) { return `${scope.mangledName}`; } if (scope instanceof GlobalScope) { return `${scope.mangledName}`; } if (scope instanceof BlockScope) { const nearstScope = scope.getNearestFunctionScope(); // global scope if (nearstScope === null) { return `${scope.mangledName}`; } else { return this.getScopeName(nearstScope); } } if (scope instanceof ClassScope) { return `${scope.mangledName}`; } if (scope instanceof NamespaceScope) { return `${scope.mangledName}`; } return 'unknown scope'; } } ================================================ FILE: src/semantics/builder_context.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import ts from 'typescript'; import { Logger } from '../log.js'; import { Scope, ScopeKind, NamespaceScope, GlobalScope, FunctionScope, importSearchTypes, ClassScope, } from '../scope.js'; import { Variable } from '../variable.js'; import { Type, TypeKind, TSClass } from '../type.js'; import { SemanticsValue, SemanticsValueKind, VarValue } from './value.js'; import { ObjectType, ValueType } from './value_types.js'; import { SemanticsNode, ModuleNode, FunctionDeclareNode, VarDeclareNode, } from './semantics_nodes.js'; import { ObjectDescriptionType, ObjectDescription } from './runtime.js'; import { clearBuiltinTypes, clearSpecializeList } from './builtin.js'; import { CustomTypeId } from '../utils.js'; import { Expression } from '../expression.js'; import { buildExpression } from './expression_builder.js'; export type SymbolKey = Variable | Scope | Type | Expression; export type SymbolValue = SemanticsValue | ValueType | SemanticsNode; export interface BuildEnv { scope: Scope; symbols: Map; tmpVarLen: number; varsLen: number; namedSymbols?: Map; function?: FunctionDeclareNode; closures?: Map; } export enum ValueReferenceKind { LEFT, RIGHT, } export function SymbolKeyToString(key?: SymbolKey): string { if (!key) return 'NULL'; if (key! instanceof Variable) { const v = key! as Variable; const scope = SymbolKeyToString(v.scope ?? undefined); return `[VAR ${v.varName}(${v.mangledName}) ${SymbolKeyToString( v.varType, )} ${scope}]`; } else if (key! instanceof Type) { const t = key! as Type; if (key.kind == TypeKind.CLASS) return `[Class ${(t as TSClass).typeId}]`; return `[Type ${t.kind}]`; } else if (key! instanceof Scope) { const s = key! as Scope; return `[Scope: ${s.kind} ${s.getName()}]`; } return `Unknown type ${key}`; } export interface Task { (): void; } export class BuildContext { globalSymbols: Map = new Map(); private tasks: Task[] = []; public objectDescriptions = new Map(); // remember the named class, functions public namedGlobalValues = new Map(); public enterScope: GlobalScope | undefined = undefined; public startStmts = new Map(); public recClassTypeGroup = new Array(); // record objectDescription and corresponding objectType public metaAndObjectTypeMap = new Map(); addFunctionValue(var_func: VarValue) { this.namedGlobalValues.set(var_func.index as string, var_func); } addClassValue(var_class: VarValue) { this.namedGlobalValues.set(var_class.index as string, var_class); } addGlobalValue(name: string, v: VarValue) { this.namedGlobalValues.set(name, v); } getGlobalValue(name: string): SemanticsValue | undefined { return this.namedGlobalValues.get(name); } pushTask(task: Task) { this.tasks.push(task); } runAllTasks() { while (this.tasks.length > 0) { const task = this.tasks.shift(); if (task) task(); } } stackEnv: BuildEnv[] = []; valueReferenceStack: ValueReferenceKind[] = []; constructor(private typeIdx: number, public module: ModuleNode) {} nextTypeId(): number { const typeId = this.typeIdx; this.typeIdx++; return typeId; } currentReference(): ValueReferenceKind { if (this.valueReferenceStack.length == 0) return ValueReferenceKind.RIGHT; return this.valueReferenceStack[this.valueReferenceStack.length - 1]; } pushReference(type: ValueReferenceKind) { this.valueReferenceStack.push(type); } popReference() { this.valueReferenceStack.pop(); } push( scope: Scope, symbols?: Map, func?: FunctionDeclareNode, ) { const env: BuildEnv = { scope: scope, symbols: symbols ? symbols : new Map(), tmpVarLen: 0, varsLen: 0, function: func ? func : undefined, }; this.updateNamedSymbol(env); this.stackEnv.push(env); } pop() { this.stackEnv.pop(); } top(): BuildEnv { return this.stackEnv[this.stackEnv.length - 1]; } updateNamedSymbolByScope( scope: Scope, symbols: Map, ) { for (let i = this.stackEnv.length - 1; i >= 0; i--) { if (this.stackEnv[i].scope === scope) { symbols.forEach((value, key) => { this.stackEnv[i].symbols.set(key, value); }); this.updateNamedSymbol(this.stackEnv[i]); break; } } } updateNamedSymbol(env: BuildEnv) { if (!env.namedSymbols) env.namedSymbols = new Map(); env.symbols.forEach((v, k) => { let symbolName: string | undefined = undefined; if (k instanceof Variable) { symbolName = (k as Variable).varName; } else if (k instanceof Scope) { symbolName = (k as Scope).getName(); } if (symbolName) { env.namedSymbols!.set(symbolName, v); } }); } private getOwnerFunctionEnv(idx: number): BuildEnv | undefined { if (idx >= this.stackEnv.length) return undefined; for (let i = idx; i >= 0; i--) { if (this.stackEnv[i].function) return this.stackEnv[i]; } return undefined; } currentFunction(): FunctionDeclareNode | undefined { const env = this.getOwnerFunctionEnv(this.stackEnv.length - 1); return env ? env.function : undefined; } getScopeNamespace(): string { const ns: string[] = []; for (let i = this.stackEnv.length - 1; i >= 0; i--) { const scope = this.stackEnv[i].scope; if (scope.kind == ScopeKind.GlobalScope) { ns.unshift((scope as GlobalScope).moduleName); } else if (scope.kind == ScopeKind.NamespaceScope) { ns.unshift((scope as NamespaceScope).getName()); } } return ns.join('|'); } private getSymbolKeyByName( id: string, search_type = importSearchTypes.All, ): SymbolKey | undefined { const scope = this.top().scope; return scope.findIdentifier(id, true, search_type); } findSymbol( id: string, search_type = importSearchTypes.All, ): SymbolValue | undefined { const name = this.getSymbolKeyByName(id, search_type); if (!name) { Logger.error(`Unknown identifier name "${name}"`); return undefined; } return this.findSymbolKey(name!); } findVariable(id: string): SymbolValue | undefined { return this.findSymbol(id, importSearchTypes.Variable); } findType(id: string): SymbolValue | undefined { return this.findSymbol(id, importSearchTypes.Type); } findFunction(id: string): SymbolValue | undefined { return this.findSymbol(id, importSearchTypes.Function); } findClass(id: string): SymbolValue | undefined { return this.findSymbol(id, importSearchTypes.Class); } findNamespace(id: string): SymbolValue | undefined { return this.findSymbol(id, importSearchTypes.Namespace); } findSymbolKey(name: SymbolKey): SymbolValue | undefined { if (name instanceof Expression) { return buildExpression(name, this); } let found: SymbolValue | undefined = undefined; const curFunc = this.currentFunction(); for (let i = this.stackEnv.length - 1; i >= 0; i--) { const env = this.stackEnv[i]; Logger.debug( `=== findSymbolKey scope[${i}] ${SymbolKeyToString( env.scope, )}, ${env.symbols}`, ); env.symbols.forEach((v, k) => Logger.debug( `=== findSymbolKey symbols ${SymbolKeyToString( k, )}, ${v.toString()}`, ), ); found = env.symbols.get(name); if (!found && env.closures) { // try find in closure found = env.closures.get(name); } if (found && found instanceof VarValue) { const env = this.getOwnerFunctionEnv(i); if (curFunc && env && curFunc !== env!.function) { // we found a closure to build the closure return this.buildClosure( name as Variable, i, found as VarValue, env.scope as FunctionScope, curFunc, ); } } if (found) return found; } found = this.globalSymbols.get(name); if (found) return found; if (name instanceof Type) { found = this.module.findValueTypeByType(name as Type); } return found; } private buildClosure( name: Variable, env_idx: number, value: VarValue, ownScope: FunctionScope, curFunc: FunctionDeclareNode, ): VarValue { if ( value.kind == SemanticsValueKind.GLOBAL_VAR || value.kind == SemanticsValueKind.GLOBAL_CONST ) { return value; } if (!(value.ref instanceof VarDeclareNode)) return value; const var_decl = value.ref as VarDeclareNode; var_decl.setUsedByClosureFunction(); let new_var_decl = var_decl; let newValue = value; ownScope = ownScope.getRootFunctionScope()!; for (let i = env_idx + 1; i < this.stackEnv.length; i++) { const env = this.stackEnv[i]; if (env.function) { /* Closure variables can only be used within the same rootFunction */ if (env.scope.getRootFunctionScope()! === ownScope) { /* The found varDeclareNode needs a deep copy, since the arribute is different between various scopes */ new_var_decl = var_decl.copy(); new_var_decl.setUsedByClosureFunction(); new_var_decl.curCtx = env.function.varList![0]; newValue = value.copy(); newValue.ref = new_var_decl; env.function.pushClosureVarDeclare(new_var_decl); if (!env.closures) env.closures = new Map(); env.closures!.set(name, newValue); } } } return newValue; } findValueTypeByKey(name: SymbolKey): ValueType | undefined { const value = this.findSymbolKey(name); if (!value) return undefined; if (value instanceof ValueType) return value as ValueType; if (value instanceof VarDeclareNode) return (value as VarDeclareNode).type; if (value instanceof SemanticsValue) return (value as SemanticsValue).type; return undefined; } findValueType(type: Type): ValueType | undefined { if (type.kind == TypeKind.CLASS || type.kind == TypeKind.INTERFACE) { const clazz = type as TSClass; let mangledName = clazz.mangledName; if (mangledName.length == 0) mangledName = clazz.className; return this.module.namedTypes.get(mangledName); } return this.findValueTypeByKey(type); } setNamedValueType(name: string, vt: ValueType) { this.module.namedTypes.set(name, vt); } getNamedValueType(name: string): ValueType | undefined { return this.module.namedTypes.get(name); } buildClosureInitList(func: FunctionDeclareNode): VarValue[] | undefined { const closure_decl = func.closureVars; if (!closure_decl) return undefined; const init_list = new Array(closure_decl!.length); for (let i = this.stackEnv.length - 1; i >= 0; i--) { const env = this.stackEnv[i]; env.symbols.forEach((v, k) => { if ( v instanceof VarValue && (v as VarValue).ref instanceof VarDeclareNode ) { const val = v as VarValue; const var_decl = val.ref as VarDeclareNode; if (var_decl.isUsedInClosureFunction()) { const idx = func.findClosureIndex(var_decl); if (idx >= 0) init_list[idx] = val; } } }); if (env.closures) { env.closures.forEach((v, k) => { const decl = v.ref as VarDeclareNode; if (decl.isUsedInClosureFunction()) { const idx = closure_decl!.indexOf(decl); if (idx >= 0) init_list[idx] = v; } }); } } return init_list; } finishBuild() { this.objectDescriptions.forEach((od, key) => { this.module.objectDescriptions.push(od); }); this.namedGlobalValues.forEach((v, _) => { if (v.valueAccessCount > 0) this.module.globalValues.push(v as VarValue); }); // clearBuiltinTypes(); clearSpecializeList(); } } ================================================ FILE: src/semantics/builtin.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { ValueTypeKind, ValueType, ArrayType, MapType, SetType, Primitive, FunctionType, ObjectType, ObjectTypeFlag, TypeParameterType, } from './value_types.js'; import { DefaultTypeId, PredefinedTypeId } from '../utils.js'; import { ObjectDescription, ObjectDescriptionType, Shape } from './runtime.js'; export function IsBuiltInObjectType(kind: ValueTypeKind): boolean { return ( kind == ValueTypeKind.ARRAY || kind == ValueTypeKind.SET || kind == ValueTypeKind.MAP || kind == ValueTypeKind.STRING || // string and raw string is object kind == ValueTypeKind.RAW_STRING || kind == ValueTypeKind.OBJECT ); } export function IsBuiltInType(kind: ValueTypeKind): boolean { return ( (kind >= ValueTypeKind.PRIMITVE_BEGIN && kind < ValueTypeKind.PRIMITVE_END) || IsBuiltInObjectType(kind) ); } export function IsBuiltInTypeButAny(kind: ValueTypeKind): boolean { return kind != ValueTypeKind.ANY && IsBuiltInType(kind); } interface ObjectInfo { type: ObjectDescriptionType; id: number; inst_name: string; class_name: string; has_generic?: boolean; } export interface SpecializeInfo { (): void; } export const builtin_objects: { [key: string]: ObjectInfo } = { Array: { type: ObjectDescriptionType.OBJECT_INSTANCE, id: PredefinedTypeId.ARRAY, inst_name: 'Array', class_name: 'ArrayConstructor', has_generic: true, }, ArrayConstructor: { type: ObjectDescriptionType.OBJECT_CLASS, id: PredefinedTypeId.ARRAY_CONSTRUCTOR, inst_name: 'Array', class_name: 'ArrayConstructor', }, String: { type: ObjectDescriptionType.OBJECT_INSTANCE, id: PredefinedTypeId.STRING_OBJECT, inst_name: 'String', class_name: 'StringConstructor', }, StringConstructor: { type: ObjectDescriptionType.OBJECT_CLASS, id: PredefinedTypeId.STRING_CONSTRUCTOR, inst_name: 'String', class_name: 'StringConstructor', }, Promise: { type: ObjectDescriptionType.OBJECT_INSTANCE, id: PredefinedTypeId.PROMISE, inst_name: 'Promise', class_name: 'PromiseConstructor', has_generic: true, }, PromiseConstructor: { type: ObjectDescriptionType.OBJECT_CLASS, id: PredefinedTypeId.PROMISE_CONSTRUCTOR, inst_name: 'Promise', class_name: 'PromiseConstructor', }, Date: { type: ObjectDescriptionType.OBJECT_INSTANCE, id: PredefinedTypeId.DATE, inst_name: 'Date', class_name: 'DateConstructor', }, DateConstructor: { type: ObjectDescriptionType.OBJECT_CLASS, id: PredefinedTypeId.DATE_CONSTRUCTOR, inst_name: 'Date', class_name: 'DateConstructor', }, Map: { type: ObjectDescriptionType.OBJECT_INSTANCE, id: PredefinedTypeId.MAP, inst_name: 'Map', class_name: 'MapConstructor', has_generic: true, }, MapConstructor: { type: ObjectDescriptionType.OBJECT_CLASS, id: PredefinedTypeId.MAP_CONSTRUCTOR, inst_name: 'Map', class_name: 'MapConstructor', }, Set: { type: ObjectDescriptionType.OBJECT_INSTANCE, id: PredefinedTypeId.SET, inst_name: 'Set', class_name: 'SetConstructor', has_generic: true, }, SetConstructor: { type: ObjectDescriptionType.OBJECT_CLASS, id: PredefinedTypeId.SET_CONSTRUCTOR, inst_name: 'Set', class_name: 'SetConstructor', }, Error: { type: ObjectDescriptionType.OBJECT_INSTANCE, id: PredefinedTypeId.ERROR, inst_name: 'Error', class_name: 'ErrorConstructor', has_generic: false, }, ErrorConstructor: { type: ObjectDescriptionType.OBJECT_CLASS, id: PredefinedTypeId.ERROR_CONSTRUCTOR, inst_name: 'Error', class_name: 'ErrorConstructor', }, ArrayBuffer: { type: ObjectDescriptionType.OBJECT_INSTANCE, id: PredefinedTypeId.ARRAYBUFFER, inst_name: 'ArrayBuffer', class_name: 'ArrayBufferConstructor', has_generic: false, }, ArrayBufferConstructor: { type: ObjectDescriptionType.OBJECT_CLASS, id: PredefinedTypeId.ARRAYBUFFER_CONSTRUCTOR, inst_name: 'ArrayBuffer', class_name: 'ArrayBufferConstructor', }, DataView: { type: ObjectDescriptionType.OBJECT_INSTANCE, id: PredefinedTypeId.DATAVIEW, inst_name: 'DataView', class_name: 'DataViewConstructor', has_generic: false, }, DataViewConstructor: { type: ObjectDescriptionType.OBJECT_CLASS, id: PredefinedTypeId.DATAVIEW_CONSTRUCTOR, inst_name: 'DataView', class_name: 'DataViewConstructor', }, }; export function IsBuiltinObject(name: string): boolean { return builtin_objects[name] != undefined; } export const builtinTypes = new Map(); const specializeList = new Map(); export function clearBuiltinTypes() { builtinTypes.clear(); } export function clearSpecializeList() { specializeList.clear(); } function getObjectAndConstructorInfos(name: string): [ObjectInfo, ObjectInfo] { const info = builtin_objects[name]; if (info.type == ObjectDescriptionType.OBJECT_INSTANCE) { return [info, builtin_objects[info.class_name!]]; } return [builtin_objects[info.inst_name!], info]; } function ObjectDescriptionFlagToObjectTypeFlag( flags: ObjectDescriptionType, ): ObjectTypeFlag { if (flags == ObjectDescriptionType.OBJECT_INSTANCE) return ObjectTypeFlag.OBJECT; if (flags == ObjectDescriptionType.OBJECT_CLASS) return ObjectTypeFlag.CLASS; return ObjectTypeFlag.LITERAL; } function init_generic(info: ObjectInfo, type: ObjectType) { if (info.has_generic) { type.setTypeArguments([]); } } function createObjectTypes( inst_info: ObjectInfo, class_info: ObjectInfo, inst_meta: ObjectDescription, class_meta: ObjectDescription, ): [ObjectType, ObjectType] { let types: [ObjectType, ObjectType] | undefined = undefined; const inst_flags = ObjectDescriptionFlagToObjectTypeFlag(inst_info.type); const class_flags = ObjectDescriptionFlagToObjectTypeFlag(class_info.type); if (inst_info.id == PredefinedTypeId.ARRAY) { types = [ new ArrayType(inst_info.id, inst_meta, inst_flags), new ArrayType(class_info.id, class_meta, class_flags), ]; } else { types = [ new ObjectType(inst_info.id, inst_meta, inst_flags), new ObjectType(class_info.id, class_meta, class_flags), ]; } init_generic(inst_info, types[0]); init_generic(class_info, types[1]); return types!; } export function GetBuiltinObjectType(name: string): ObjectType { const type = builtinTypes.get(name); if (type) return type; // create the builtin Object //const names = getObjectAndConstructorName(name); //const name = names[0]; //const ctr_name = names[1]; const [inst_info, class_info] = getObjectAndConstructorInfos(name); const inst_meta = new ObjectDescription( inst_info.inst_name, ObjectDescriptionType.OBJECT_INSTANCE, ); const class_meta = new ObjectDescription( inst_info.class_name, ObjectDescriptionType.OBJECT_CLASS, ); class_meta.instance = inst_meta; inst_meta.clazz = class_meta; inst_meta.setBuiltin(); class_meta.setBuiltin(); const [inst_type, class_type] = createObjectTypes( inst_info, class_info, inst_meta, class_meta, ); inst_type.classType = class_type; class_type.instanceType = inst_type; inst_type.setBuiltin(); class_type.setBuiltin(); builtinTypes.set(inst_info.inst_name, inst_type); builtinTypes.set(inst_info.class_name, class_type); return builtinTypes.get(name)!; } export function GetAndRemoveObjectSpecializeList( obj_type: ObjectType, ): SpecializeInfo[] | undefined { const list = specializeList.get(obj_type); specializeList.delete(obj_type); return list; } export function ProcessBuiltinObjectSpecializeList() { specializeList.forEach((list, obj) => { for (const cb of list) cb(); }); specializeList.clear(); } export function AddSpecializeObjectType( obj_type: ObjectType, info: SpecializeInfo, ) { if (specializeList.has(obj_type)) { specializeList.get(obj_type)!.push(info); } else { specializeList.set(obj_type, [info]); } } export function ForEachBuiltinObject(cb: (obj_type: ObjectType) => void) { builtinTypes.forEach((obj_type, name) => cb(obj_type)); } ///////////////////////////////////////////////////////////////// export function GetShapeFromType( type: ValueType, isThisShape = true, ): Shape | undefined { switch (type.kind) { case ValueTypeKind.OBJECT: case ValueTypeKind.ARRAY: case ValueTypeKind.MAP: case ValueTypeKind.SET: return isThisShape ? (type as ObjectType).meta.thisShape : (type as ObjectType).meta.originShape; case ValueTypeKind.FUNCTION: // TODO break; case ValueTypeKind.RAW_STRING: // TODO case ValueTypeKind.STRING: // TODO return GetBuiltinObjectType('String').meta.originShape; } return undefined; } ================================================ FILE: src/semantics/dump.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export interface DumpWriter { write(str: string): void; shift(): void; unshift(): void; } export function CreateDefaultDumpWriter(): DumpWriter { let prefix = ''; return { write: (s: string) => console.log(`${prefix}${s}`), shift: () => (prefix = prefix + '\t'), unshift: () => (prefix = prefix.slice(1)), }; } ================================================ FILE: src/semantics/expression_builder.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import ts from 'typescript'; import { ValueType, ValueTypeKind, PrimitiveType, Primitive, ArrayType, SetType, MapType, UnionType, TypeParameterType, FunctionType, ClosureContextType, EnumType, ObjectType, ValueTypeWithArguments, WASMArrayType, TupleType, WASMStructType, } from './value_types.js'; import { PredefinedTypeId, getNodeLoc, isTypeGeneric } from '../utils.js'; import { Logger } from '../log.js'; import { createType, isObjectType, isNullValueType, createUnionType, createArrayType, createObjectType, SpecializeTypeMapper, CreateWideTypeFromTypes, createTupleType, } from './type_creator.js'; import { GetPredefinedType } from './predefined_types.js'; import { DumpWriter, CreateDefaultDumpWriter } from './dump.js'; import { SemanticsValueKind, SemanticsValue, NopValue, VarValue, VarValueKind, ThisValue2, SuperValue, LiteralValue, BinaryExprValue, PrefixUnaryExprValue, PostUnaryExprValue, ConditionExprValue, CastValue, NewClassValue, InstanceOfValue, FunctionCallBaseValue, ElementSetValueKind, ElementSetValue, ElementGetValueKind, ElementGetValue, FunctionCallValue, ConstructorCallValue, ToStringValue, ValueBinaryOperator, NewClosureFunction, UnimplementValue, DynamicSetValue, DynamicGetValue, DynamicCallValue, ShapeSetValue, ShapeGetValue, ShapeCallValue, VTableGetValue, VTableCallValue, VTableSetValue, OffsetCallValue, OffsetGetValue, OffsetSetValue, OffsetSetterValue, OffsetGetterValue, DirectCallValue, DirectSetterValue, DirectGetterValue, DirectGetValue, MemberGetValue, MemberSetValue, MemberCallValue, NewLiteralObjectValue, NewLiteralArrayValue, NewConstructorObjectValue, NewFromClassObjectValue, ClosureCallValue, NewArrayValue, NewArrayLenValue, TypeofValue, AnyCallValue, SuperUsageFlag, CommaExprValue, SpreadValue, TemplateExprValue, EnumerateKeysGetValue, } from './value.js'; import { SemanticsNode, SemanticsKind, FunctionDeclareNode, VarDeclareNode, ModuleNode, } from './semantics_nodes.js'; import { Expression, NullKeywordExpression, NumberLiteralExpression, StringLiteralExpression, ObjectLiteralExpression, ArrayLiteralExpression, FalseLiteralExpression, TrueLiteralExpression, IdentifierExpression, BinaryExpression, UnaryExpression, ConditionalExpression, CallExpression, SuperExpression, PropertyAccessExpression, NewExpression, ParenthesizedExpression, ElementAccessExpression, AsExpression, FunctionExpression, TypeOfExpression, CommaExpression, SpreadExpression, TemplateExpression, EnumerateKeysExpression, } from '../expression.js'; import { Scope, ScopeKind, ClassScope, FunctionScope, NamespaceScope, GlobalScope, importSearchTypes, } from '../scope.js'; import { Type, TSClass, TypeKind, TSArray, TSTuple, WasmStructType, WasmArrayType, } from '../type.js'; import { BuildContext, ValueReferenceKind, SymbolKeyToString, SymbolValue, } from './builder_context.js'; import { GetShapeFromType, builtinTypes } from './builtin.js'; import { MemberType, ShapeMember, ShapeMethod, ShapeField, ShapeAccessor, Shape, ObjectDescription, MemberDescription, use_shape, } from './runtime.js'; import { processEscape } from '../utils.js'; import { BuiltinNames } from '../../lib/builtin/builtin_name.js'; import { getConfig } from '../../config/config_mgr.js'; import { UnimplementError } from '../error.js'; function isInt(expr: Expression): boolean { /* TODO: currently we treat all numbers as f64, we can make some analysis and optimize some number to int */ if (expr.tsNode) { // if NumberLiteralExpression represents an array index,we should treat this number as i32 if (expr.tsNode.parent?.kind == ts.SyntaxKind.ElementAccessExpression) return true; } return false; } function toInt(n: number): number { return n | 0; } function flattenOperator(opKind: ts.SyntaxKind) { switch (opKind) { case ts.SyntaxKind.PlusEqualsToken: { return ts.SyntaxKind.PlusToken; } case ts.SyntaxKind.MinusEqualsToken: { return ts.SyntaxKind.MinusToken; } case ts.SyntaxKind.AsteriskAsteriskEqualsToken: { return ts.SyntaxKind.AsteriskAsteriskToken; } case ts.SyntaxKind.AsteriskEqualsToken: { return ts.SyntaxKind.AsteriskToken; } case ts.SyntaxKind.SlashEqualsToken: { return ts.SyntaxKind.SlashToken; } case ts.SyntaxKind.PercentEqualsToken: { return ts.SyntaxKind.PercentToken; } case ts.SyntaxKind.AmpersandEqualsToken: { return ts.SyntaxKind.AmpersandToken; } case ts.SyntaxKind.BarEqualsToken: { return ts.SyntaxKind.BarToken; } case ts.SyntaxKind.CaretEqualsToken: { return ts.SyntaxKind.CaretToken; } case ts.SyntaxKind.LessThanLessThanEqualsToken: { return ts.SyntaxKind.LessThanLessThanToken; } case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: { return ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken; } case ts.SyntaxKind.GreaterThanGreaterThanEqualsToken: { return ts.SyntaxKind.GreaterThanGreaterThanToken; } case ts.SyntaxKind.BarBarEqualsToken: { return ts.SyntaxKind.BarBarToken; } case ts.SyntaxKind.AmpersandAmpersandEqualsToken: { return ts.SyntaxKind.AmpersandAmpersandToken; } case ts.SyntaxKind.QuestionQuestionEqualsToken: { return ts.SyntaxKind.QuestionQuestionToken; } case ts.SyntaxKind.PlusPlusToken: { return ts.SyntaxKind.PlusEqualsToken; } case ts.SyntaxKind.MinusMinusToken: { return ts.SyntaxKind.MinusEqualsToken; } case ts.SyntaxKind.MinusToken: { return ts.SyntaxKind.MinusToken; } default: return opKind; } } function getValueFromNamespace( ns: VarValue, member: string, context: BuildContext, ): SemanticsValue { if (ns.type.kind != ValueTypeKind.NAMESPACE) { throw Error(`${ns} is not a namespace`); } const ns_scope = ns.ref as Scope; Logger.debug(`=== Namespace ${ns_scope.kind} member: ${member}`); // find the name in the ns_scope let name = ns_scope.findIdentifier( member, true, importSearchTypes.All, true, ); if (!name && ns_scope.kind == ScopeKind.GlobalScope) { const target_name = (ns_scope as GlobalScope).nameAliasExportMap.get( member, ); if (target_name) { name = ns_scope.findIdentifier(target_name, true); } } if (!name) { throw Error(`cannot find "${member}" in ${ns}`); } const value = context.globalSymbols.get(name!); if (!value) { throw Error(`undefiend "${member}" in ${ns} (${name})`); } const result = SymbolValueToSemanticsValue(value!, context); if (result) return result; throw Error(`cannot create value "${member}" in ${ns}(${name})`); } function buildPropertyAccessExpression( expr: PropertyAccessExpression, context: BuildContext, ): SemanticsValue { // whether the context of property access is in the call expression let isMethodCall = false; if ( expr.parent && expr.parent.expressionKind == ts.SyntaxKind.CallExpression ) { isMethodCall = true; } context.pushReference(ValueReferenceKind.RIGHT); let own = buildExpression(expr.propertyAccessExpr, context); context.popReference(); let own_name = ''; if (expr.propertyAccessExpr.expressionKind == ts.SyntaxKind.Identifier) { const identifer = expr.propertyAccessExpr as IdentifierExpression; own_name = identifer.identifierName; } if (own.kind == SemanticsValueKind.NOP) { throw Error(`buildPropertyAccessExpression: Got NopValue`); return own; } let member_name = (expr.propertyExpr as IdentifierExpression) .identifierName; const type = own.effectType; /** * e.g. * class A { * x: number; * constructor(x: number) { * this.x = x; * } * * func(param: T) { * return param; * } * } * const a: A = new A(1); * const ret = a.func(2); */ if (isMethodCall) { if ( expr.parent && type instanceof ObjectType && !type.genericOwner && isTypeGeneric(expr.propertyExpr.exprType) ) { // find the specialized method that needs to be called through the arguments list const callExpr = expr.parent as CallExpression; if (callExpr.callArgs && callExpr.callArgs.length > 0) { const func_type = createType( context, expr.propertyExpr.exprType, ) as FunctionType; const paramterTypes = func_type.argumentsType; const argumentTypes: ValueType[] = []; const typeArguments: ValueType[] = []; for (let i = 0; i < callExpr.callArgs.length; i++) { const arg = callExpr.callArgs[i]; argumentTypes.push(buildExpression(arg, context).type); } for (let i = 0; i < paramterTypes.length; i++) { if (paramterTypes[i].kind == ValueTypeKind.TYPE_PARAMETER) { if ( argumentTypes[i].kind !== ValueTypeKind.TYPE_PARAMETER ) typeArguments.push(argumentTypes[i]); } else if (paramterTypes[i].kind == ValueTypeKind.ARRAY) { const elementType = (argumentTypes[i] as ArrayType) .element; if (elementType.kind !== ValueTypeKind.TYPE_PARAMETER) typeArguments.push(elementType); } const typeNames = new Array(); typeArguments.forEach((v) => { const name = `${ValueTypeKind[v.kind]}`; typeNames.push(name.toLowerCase()); }); if (typeNames.length > 0) { const typeSignature = '<' + typeNames.join(',') + '>'; member_name = member_name + typeSignature; } } } } } if (type.kind == ValueTypeKind.ENUM) { const enum_type = type as EnumType; const value = enum_type.members.get(member_name); if (value === undefined) { throw Error(`Unkonw enum member "${member_name}" in ${type}`); } if (typeof value == 'string') { return new LiteralValue(Primitive.String, value); } else { return new LiteralValue(Primitive.Int, value); } } else if (type.kind == ValueTypeKind.NAMESPACE) { return getValueFromNamespace(own as VarValue, member_name, context); } const ref_type = context.currentReference(); const member_as_write = ref_type == ValueReferenceKind.LEFT; own.incAccessCount(); Logger.debug( `==== buildPropertyAccessExpression own: ${own} shape: ${own.shape}`, ); if (own instanceof VarValue && !(own as VarValue).isConst) { /* the purpose of copying is to prevent subsequent changes to VarValue from affecting this semantic processing */ /* e.g. let a: A | B = new A(); let x = a.x; a = new B(); let y = a.y; A and B are class types. When the “new A()” statement is executed, the Shape of “a” at this time represents A; Later, when the “new B()” statement is executed, the shape of “a” at this time represents B; Then WASMExpressionGen is executed which will translate semanticsValue to WASM code, it needs to find the corresponding VarValue through the identifier name "a". At this time, the Shape in the VarValue corresponding to “a” is the shape of B when it was last assigned. So when executing "a.x", it means to get attribute x in class B, not in class A. In order to avoid this situation, we need to copy the value of “own”. */ own = (own as VarValue).copy(); } const shape = own.shape; if (!shape || own.type.kind == ValueTypeKind.ANY) { Logger.warn(`WARNING Type has null shape ${type}, use dynamic Access`); const dynamicAccess = createDynamicAccess( own, member_name, member_as_write, isMethodCall, ); if (own.type instanceof ObjectType) { /* Workaround: Array method access */ /* We still need this workaround: class A { static c = 10; //10 static readonly d = 12 + A.c; //22 } */ dynamicAccess.type = own.type.meta.findMember(member_name)?.valueType || dynamicAccess.type; } return dynamicAccess; } const meta = shape!.meta; const isThisShape = meta.isObjectInstance; let member = meta.findMember(member_name); if (!member || BuiltinNames.fallbackConstructors.includes(own_name)) { Logger.warn(`WARNING Not found the member name, use dynamic Access`); if (BuiltinNames.fallbackConstructors.includes(own_name)) { own.type.kind = ValueTypeKind.ANY; } return createDynamicAccess( own, member_name, member_as_write, isMethodCall, ); } const shape_member = shape.getMember(member.index); if (!shape_member || shape_member.isEmpty) { if (isThisShape) return createVTableAccess( own, member, member_as_write, isMethodCall, ); else return createShapeAccess( own, member, member_as_write, isMethodCall, ); } /* Workaround: For obj property dynamic access, type member is not equal with shape member, so we should use type member to get DirectAccess value */ if (type instanceof ObjectType) { const typeMeta = type.meta; const typeMember = typeMeta.findMember(member_name); if (typeMember && typeMember.valueType.kind !== member.valueType.kind) { member = typeMember; } } return createDirectAccess( context, own, shape_member, member, member_as_write, isThisShape, isMethodCall, ); } function createDynamicAccess( own: SemanticsValue, name: string, is_write: boolean, is_method_call: boolean, ): SemanticsValue { /* iff call Object builtin methods */ if (BuiltinNames.ObjectBuiltinMethods.includes(name)) { if (name === BuiltinNames.ObjectToStringMethod) { return new ToStringValue(SemanticsValueKind.VALUE_TO_STRING, own); } } if (is_write) return new DynamicSetValue(own, name); let type: ValueType | undefined = undefined; if ( own.type.kind === ValueTypeKind.WASM_ARRAY || own.type.kind === ValueTypeKind.WASM_STRUCT || own.type.kind === ValueTypeKind.TUPLE ) { if (name === 'length') { type = Primitive.Number; } } return new DynamicGetValue(own, name, is_method_call, type); } function createShapeAccess( own: SemanticsValue, member: MemberDescription, is_write: boolean, is_method_call: boolean, ): SemanticsValue { if (is_write) return new ShapeSetValue(own, member.valueType, member.index); if (member.type == MemberType.METHOD && is_method_call) { return new ShapeCallValue( own, member.valueType as FunctionType, member.index, ); } return new ShapeGetValue(own, member.valueType, member.index); } function createVTableAccess( own: SemanticsValue, member: MemberDescription, is_write: boolean, is_method_call: boolean, ): SemanticsValue { if (is_write) return new VTableSetValue(own, member.valueType, member.index); if (member.type == MemberType.METHOD && is_method_call) { return new VTableCallValue( own, member.valueType as FunctionType, member.index, ); } return new VTableGetValue(own, member.valueType, member.index); } function createDirectAccess( context: BuildContext, own: SemanticsValue, shape_member: ShapeMember, member: MemberDescription, is_write: boolean, isThisShape: boolean, is_method_call: boolean, ): SemanticsValue { if (is_write) { return createDirectSet( own, shape_member, member, isThisShape, is_method_call, ); } else { return createDirectGet( context, own, shape_member, member, isThisShape, is_method_call, ); } } function createDirectGet( context: BuildContext, own: SemanticsValue, shape_member: ShapeMember, member: MemberDescription, isThisShape: boolean, is_method_call: boolean, ) { switch (shape_member.kind) { case MemberType.FIELD: return new OffsetGetValue( own, member.valueType, (shape_member as ShapeField).offset!, ); case MemberType.ACCESSOR: { const accessor = shape_member as ShapeAccessor; const getter = accessor.getter; if (!getter) { Logger.info('==== getter is not exist, access by shape'); return new LiteralValue(Primitive.Undefined, undefined); } if (accessor.isOffset) { return new OffsetGetterValue( own, member.getterType!, accessor.getterOffset!, ); } else { const ownerType = context.metaAndObjectTypeMap.get( (own as VarValue).shape!.meta, )!; const getterOwnerType = ( (getter as VarValue).ref as FunctionDeclareNode ).thisClassType!.instanceType!; // if the value of 'isOwn' is false, it means that the getter and setter are both not reimplemented in the sub class. // when only the setter is reimplemented in the sub class and the getter is inherited from the base class, the getter returns undefined. if (member.isOwn && !ownerType.equals(getterOwnerType)) return new LiteralValue(Primitive.Undefined, undefined); return new DirectGetterValue( own, member.getterType!, accessor.getterValue!, ); } } case MemberType.METHOD: { const method = shape_member as ShapeMethod; const func_type = member.valueType as FunctionType; if (method.isOffset) { if (is_method_call) { return new OffsetCallValue( own, func_type, method.methodOffset!, ); } else { return new OffsetGetValue( own, func_type, (shape_member as ShapeMethod).methodOffset!, ); } } else { if (is_method_call) { return new DirectCallValue( own, func_type, method.methodValue!, ); } else { return new DirectGetValue( own, member.valueType, member.index, ); } } break; } } throw Error(`wrong access`); } function createDirectSet( own: SemanticsValue, shape_member: ShapeMember, member: MemberDescription, isThisShape: boolean, is_method_call: boolean, ) { switch (shape_member.kind) { case MemberType.FIELD: // get the feild directly return new OffsetSetValue( own, member.valueType, (shape_member as ShapeField).offset!, ); case MemberType.ACCESSOR: { const accessor = shape_member as ShapeAccessor; const setter = accessor.setter; if (!setter) { Logger.info('==== setter is not exist, access by shape'); if (isThisShape) return createVTableAccess(own, member, true, true); return createShapeAccess(own, member, true, true); } if (accessor.isOffset) { return new OffsetSetterValue( own, member.setterType!, accessor.setterOffset!, accessor.getterOffset, ); } else { return new DirectSetterValue( own, member.setterType!, accessor.setterValue!, accessor.getterValue, ); } } } throw Error(`wrong access`); } function SymbolValueToSemanticsValue( value: SymbolValue, context: BuildContext, ): SemanticsValue | undefined { if (value instanceof SemanticsValue) return value as SemanticsValue; if (value instanceof FunctionDeclareNode) { const func_node = value as FunctionDeclareNode; return new VarValue( SemanticsValueKind.GLOBAL_CONST, func_node.funcType, func_node, -1, ); } if (value instanceof VarDeclareNode) { const var_decl = value as VarDeclareNode; return new VarValue( var_decl.storageType as VarValueKind, var_decl.type, var_decl, var_decl.index, ); } if (value instanceof EnumType) { const enum_type = value as EnumType; return new VarValue( SemanticsValueKind.GLOBAL_CONST, enum_type, enum_type, -1, ); } if (value instanceof ObjectType) { const clazz_type = value as ObjectType; if (clazz_type.classType) { return new VarValue( SemanticsValueKind.GLOBAL_CONST, clazz_type.classType!, clazz_type.classType!, -1, ); } else { throw Error(`${clazz_type} Cannot load as an identifier`); } } return undefined; } function buildIdentiferExpression( expr: IdentifierExpression, context: BuildContext, ): SemanticsValue { const name = expr.identifierName; if (name == 'undefined') { return new LiteralValue(Primitive.Undefined, undefined); } if (name == 'NaN') { return new LiteralValue(Primitive.Number, NaN); } if (name == 'Infinity') { return new LiteralValue(Primitive.Number, Infinity); } let ret = context.findVariable(name); if (!ret) { Logger.debug(`=== try find identifer "${name}" as Varaible Faield`); ret = context.findFunction(name); } if (!ret) { Logger.debug(`=== try find identifer "${name}" as Function Faield`); ret = context.findClass(name); } if (!ret) { Logger.debug(`=== try find identifer "${name}" as Class Faield`); ret = builtinTypes.get(name) ? builtinTypes.get(name) : context.findType(name); } if (!ret) { Logger.debug(`=== try find identifer "${name}" as Type Faield`); ret = context.findNamespace(name); } if (ret) { Logger.debug(`=== found identifer "${name}" ${ret}`); const result = SymbolValueToSemanticsValue(ret, context); if (result) return result; } throw Error(`Cannot find the idenentifier "${name}"`); } function buildTypeOfExpression( expr: TypeOfExpression, context: BuildContext, ): SemanticsValue { const typeKind = expr.expr.exprType.kind; let res: SemanticsValue; switch (typeKind) { case TypeKind.STRING: res = new LiteralValue(Primitive.String, 'string'); break; case TypeKind.NUMBER: res = new LiteralValue(Primitive.String, 'number'); break; case TypeKind.BOOLEAN: res = new LiteralValue(Primitive.String, 'boolean'); break; case TypeKind.UNDEFINED: res = new LiteralValue(Primitive.String, 'undefined'); break; case TypeKind.FUNCTION: res = new LiteralValue(Primitive.String, 'function'); break; case TypeKind.ARRAY: case TypeKind.ENUM: case TypeKind.INTERFACE: case TypeKind.CLASS: case TypeKind.NULL: res = new LiteralValue(Primitive.String, 'object'); break; case TypeKind.UNION: case TypeKind.ANY: { res = new TypeofValue(buildExpression(expr.expr, context)); break; } default: throw new Error(`unimpl typeof's expression type ${typeKind}`); } return res; } // convert `Hello ${name} World` to the format // "Hello " [name, " World"], and then call // `String.concat` to concat them in the backend function buildTemplateExpression( expr: TemplateExpression, context: BuildContext, ): SemanticsValue { const head = buildExpression(expr.head, context); const follows: SemanticsValue[] = []; for (const span of expr.spans) { let expr = buildExpression(span.expr, context); expr = newCastValue(Primitive.String, expr); follows.push(expr); const middle_or_tail = buildExpression(span.literal, context); follows.push(middle_or_tail); } return new TemplateExprValue(head, follows); } function findObjectLiteralType( type: TSClass, context: BuildContext, ): ObjectType { const vt = context.findValueType(type); if (vt) return vt as ObjectType; const obj_type = createObjectType(type, context)!; context.runAllTasks(); return obj_type; } function buildObjectLiteralExpression( expr: ObjectLiteralExpression, context: BuildContext, ): SemanticsValue { const type = expr.exprType as TSClass; const object_type = findObjectLiteralType(type as TSClass, context); const value = new NewLiteralObjectValue(object_type); /* eg. arr = [{a:1}, {a:2}, {a:3, b:4}] TSC treate arr type is Array<{a:number, b?: number} | {a:number, b:number}> */ const meta = object_type.meta; for (let i = 0; i < expr.objectFields.length; i++) { const name_ = expr.objectFields[i].identifierName; const name = processEscape(name_); const member = meta.findMember(name); if (member) { context.pushReference(ValueReferenceKind.RIGHT); const init_value = buildExpression(expr.objectValues[i], context); context.popReference(); value.setField(member.index, init_value); } else { throw Error(`Cannot init the ${name}@${i} of ${object_type}`); } } return value; } function buildArrayLiteralExpression( expr: ArrayLiteralExpression, context: BuildContext, ): SemanticsValue { if (expr.arrayValues.length == 0) { if ( expr.exprType instanceof TSArray && expr.exprType.elementType.kind == TypeKind.UNKNOWN ) { return new NewArrayLenValue( GetPredefinedType(PredefinedTypeId.ARRAY_ANY)! as ArrayType, new LiteralValue(Primitive.Int, 0), ); } } const init_values: SemanticsValue[] = []; let arrayLiteral_type = context.findValueType(expr.exprType); /* ArrayLiteral may be array type, and it can be tuple type */ let init_array_types: Set | undefined = undefined; if (!arrayLiteral_type || arrayLiteral_type.kind != ValueTypeKind.ARRAY) { init_array_types = new Set(); } // element type calculated from exprType let element_type: ValueType | undefined = undefined; if (arrayLiteral_type instanceof ArrayType) { element_type = (arrayLiteral_type).element; } if (expr.exprType instanceof WasmArrayType) { element_type = createType(context, expr.exprType.arrayType.elementType); } for (let i = 0; i < expr.arrayValues.length; i++) { const element = expr.arrayValues[i]; context.pushReference(ValueReferenceKind.RIGHT); let v = buildExpression(element, context); /* get element type if exprType is TSTuple */ if (expr.exprType instanceof TSTuple) { element_type = createType(context, expr.exprType.elements[i]); } else if (expr.exprType instanceof WasmStructType) { element_type = createType( context, expr.exprType.tupleType.elements[i], ); } if (element_type !== undefined) { v = newCastValue(element_type, v); } context.popReference(); init_values.push(v); /* if v is SpreadValue, add it's elem-type to init_types */ let v_type = v.type; if (v instanceof SpreadValue) { const target = v.target; if (target.type.kind == ValueTypeKind.ARRAY) { v_type = (target.type as ArrayType).element; } else if (target.type.kind == ValueTypeKind.ANY) { v_type = target.type; } } if (init_array_types) { init_array_types.add(v_type); } } if (init_array_types) { arrayLiteral_type = createArrayType( context, CreateWideTypeFromTypes(context, init_array_types), ); } if (expr.exprType instanceof TSTuple) { return new NewLiteralArrayValue( createType(context, expr.exprType), init_values, ); } else if (expr.exprType instanceof WasmStructType) { return new NewLiteralArrayValue( createType(context, expr.exprType), init_values, ); } else { const elem_type = (arrayLiteral_type as ArrayType).element; const initValues = expr.arrayValues.length == 0 ? [] : init_values.map((v) => { return elem_type.equals(v.type) ? v : newCastValue(elem_type, v); }); /* process generic array type */ if (initValues.length > 0) { // actual element type const value_type = initValues[0].type; if ( elem_type.kind == ValueTypeKind.TYPE_PARAMETER && !value_type.equals(elem_type) ) arrayLiteral_type = createArrayType(context, value_type); } let literalArrayType = arrayLiteral_type!; if (expr.exprType instanceof WasmArrayType) { literalArrayType = createType(context, expr.exprType); } return new NewLiteralArrayValue(literalArrayType, initValues); } } export function isEqualOperator(kind: ts.SyntaxKind): boolean { return ( kind == ts.SyntaxKind.EqualsToken || kind == ts.SyntaxKind.PlusEqualsToken || kind == ts.SyntaxKind.MinusEqualsToken || kind == ts.SyntaxKind.AsteriskEqualsToken || kind == ts.SyntaxKind.AsteriskAsteriskEqualsToken || kind == ts.SyntaxKind.SlashEqualsToken || kind == ts.SyntaxKind.PercentEqualsToken || kind == ts.SyntaxKind.AmpersandEqualsToken || kind == ts.SyntaxKind.AmpersandAmpersandEqualsToken || kind == ts.SyntaxKind.BarEqualsToken || kind == ts.SyntaxKind.BarBarEqualsToken || kind == ts.SyntaxKind.CaretEqualsToken ); } export function isCompareOperator(kind: ts.SyntaxKind): boolean { return ( kind == ts.SyntaxKind.LessThanEqualsToken || kind == ts.SyntaxKind.LessThanToken || kind == ts.SyntaxKind.GreaterThanEqualsToken || kind == ts.SyntaxKind.GreaterThanToken || kind == ts.SyntaxKind.EqualsEqualsToken || kind == ts.SyntaxKind.EqualsEqualsEqualsToken || kind == ts.SyntaxKind.ExclamationEqualsToken || kind == ts.SyntaxKind.ExclamationEqualsEqualsToken ); } function wrapObjToAny(value: SemanticsValue, type: ValueType) { if ( value instanceof NewLiteralObjectValue || value instanceof NewLiteralArrayValue ) { for (let i = 0; i < value.initValues.length; i++) { const elemValue = value.initValues[i]; if ( elemValue instanceof NewLiteralObjectValue || elemValue instanceof NewLiteralArrayValue ) { value.initValues[i] = wrapObjToAny(elemValue, type); } else { if (type.kind == ValueTypeKind.ANY) { value.initValues[i] = newCastValue(type, elemValue); } } } } return new CastValue(SemanticsValueKind.OBJECT_CAST_ANY, type, value); } function checkSigned(tmpValue: BinaryExprValue): boolean { if ( tmpValue.opKind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken ) { return false; } if (tmpValue.left instanceof BinaryExprValue) { tmpValue = tmpValue.left; return checkSigned(tmpValue); } if (tmpValue.right instanceof BinaryExprValue) { tmpValue = tmpValue.right; return checkSigned(tmpValue); } return true; } function judgeIsInt(value: number) { if (value >= -2147483648 && value < 2147483647 && value % 1 === 0) { return true; } else { return false; } } function judgeIsF32(value: number) { if (value >= -8388607 && value < 8388607) { return true; } else { return false; } } function checkOverflow(value: LiteralValue, type: ValueType) { if (type.kind === ValueTypeKind.INT) { return judgeIsInt(value.value as number); } else if (type.kind === ValueTypeKind.WASM_F32) { return judgeIsF32(value.value as number); } return true; } export function newCastValue( type: ValueType, value: SemanticsValue, ): SemanticsValue { if (type.equals(value.type)) return value; if (value instanceof SpreadValue) return value; else if (type.kind == ValueTypeKind.TYPE_PARAMETER) type = (type as TypeParameterType).wideType; else if (type.kind == ValueTypeKind.ENUM) type = (type as EnumType).memberType; const value_type = value.effectType; if (value_type.kind == ValueTypeKind.UNION) { if (type.kind == ValueTypeKind.ANY) { return new CastValue( SemanticsValueKind.UNION_CAST_ANY, type, value, ); } else if (type.kind == ValueTypeKind.BOOLEAN) { return new CastValue( SemanticsValueKind.UNION_CAST_VALUE, type, value, ); } else if (isObjectType(type.kind)) { return new CastValue( SemanticsValueKind.UNION_CAST_OBJECT, type, value, ); } else { return new CastValue( SemanticsValueKind.UNION_CAST_VALUE, type, value, ); } } if ( value_type.kind == ValueTypeKind.GENERIC && type.kind == ValueTypeKind.GENERIC ) { // it's template type return value; } if (type.equals(value_type)) return value; if ( type.kind == ValueTypeKind.FUNCTION && value_type.kind == ValueTypeKind.FUNCTION ) return value; if ( type.kind == ValueTypeKind.ARRAY && value_type.kind == ValueTypeKind.ARRAY ) { const arr_type = type as ArrayType; const arr_value_type = value_type as ArrayType; let arr_element_type = arr_type.element; let value_element_type = arr_value_type.element; if (arr_element_type.kind == ValueTypeKind.TYPE_PARAMETER) arr_element_type = (arr_element_type as TypeParameterType).wideType; if (value_element_type.kind == ValueTypeKind.TYPE_PARAMETER) value_element_type = (value_element_type as TypeParameterType) .wideType; if (arr_element_type.equals(value_element_type)) return value; if ( arr_element_type.kind == ValueTypeKind.ANY && value_element_type.kind == ValueTypeKind.ANY ) return value; /* TODO: need to create new CastValue from Array to Array */ if ( isObjectType(arr_element_type.kind) && isObjectType(value_element_type.kind) ) return value; if ( (arr_element_type.kind == ValueTypeKind.RAW_STRING || arr_element_type.kind == ValueTypeKind.STRING) && (value_element_type.kind == ValueTypeKind.STRING || value_element_type.kind == ValueTypeKind.RAW_STRING) ) return value; if ( (arr_element_type.kind == ValueTypeKind.NUMBER || arr_element_type.kind == ValueTypeKind.BOOLEAN || arr_element_type.kind == ValueTypeKind.INT) && (value_element_type.kind == ValueTypeKind.NUMBER || value_element_type.kind == ValueTypeKind.BOOLEAN || value_element_type.kind == ValueTypeKind.INT) ) return value; throw Error( `cannot make cast value from "${value_type}" to "${type}"`, ); } if ( (type.kind == ValueTypeKind.RAW_STRING && value_type.kind == ValueTypeKind.STRING) || (type.kind == ValueTypeKind.STRING && value_type.kind == ValueTypeKind.RAW_STRING) ) { return value; } if ( type.kind == ValueTypeKind.STRING || type.kind == ValueTypeKind.RAW_STRING ) { if (isObjectType(value_type.kind)) { return new ToStringValue( SemanticsValueKind.OBJECT_TO_STRING, value, ); } if (value_type.kind == ValueTypeKind.ANY) { return new CastValue( SemanticsValueKind.ANY_CAST_VALUE, type, value, ); } else { return new ToStringValue(SemanticsValueKind.VALUE_TO_STRING, value); } } if (type.kind == ValueTypeKind.BOOLEAN) { if (value_type.kind == ValueTypeKind.ANY) return new CastValue( SemanticsValueKind.ANY_CAST_VALUE, type, value, ); // object check is null or undefiend if (isObjectType(type.kind)) return new CastValue( SemanticsValueKind.OBJECT_CAST_VALUE, type, value, ); return new CastValue(SemanticsValueKind.VALUE_CAST_VALUE, type, value); } if ( type.kind == ValueTypeKind.INT || type.kind == ValueTypeKind.NUMBER || type.kind == ValueTypeKind.WASM_I64 || type.kind == ValueTypeKind.WASM_F32 ) { if ( value_type.kind == ValueTypeKind.NUMBER || value_type.kind == ValueTypeKind.INT || value_type.kind == ValueTypeKind.BOOLEAN || value_type.kind == ValueTypeKind.STRING || value_type.kind == ValueTypeKind.RAW_STRING || value_type.kind == ValueTypeKind.ENUM || value_type.kind == ValueTypeKind.WASM_I64 || value_type.kind == ValueTypeKind.WASM_F32 || isNullValueType(value_type.kind) ) if ( value instanceof LiteralValue && value.type.kind === ValueTypeKind.NUMBER && checkOverflow(value, type) ) { value.type = type; return value; } else { let isSigned = true; const tmpValue = value; if (tmpValue instanceof BinaryExprValue) { isSigned = checkSigned(tmpValue); } const castedValue = new CastValue( SemanticsValueKind.VALUE_CAST_VALUE, type, value, ); castedValue.isSigned = isSigned; return castedValue; } if (value_type.kind == ValueTypeKind.ANY) return new CastValue( SemanticsValueKind.ANY_CAST_VALUE, type, value, ); throw Error( `cannot make cast value from "${value_type}" to "${type}"`, ); } if (isNullValueType(type.kind)) { if (value_type.kind == ValueTypeKind.ANY) { return new CastValue( SemanticsValueKind.ANY_CAST_VALUE, type, value, ); } else if (isObjectType(value_type.kind)) { return new CastValue( SemanticsValueKind.OBJECT_CAST_VALUE, type, value, ); } return new CastValue(SemanticsValueKind.VALUE_CAST_VALUE, type, value); } if (type.kind == ValueTypeKind.ANY) { if (isObjectType(value_type.kind)) { return wrapObjToAny(value, type); } return new CastValue(SemanticsValueKind.VALUE_CAST_ANY, type, value); } if (type.kind == ValueTypeKind.UNION) { if (isObjectType(value_type.kind)) return new CastValue( SemanticsValueKind.OBJECT_CAST_UNION, type, value, ); return new CastValue(SemanticsValueKind.VALUE_CAST_UNION, type, value); } ///////// // object shape translate if ( type.kind == ValueTypeKind.OBJECT && value_type.kind == ValueTypeKind.OBJECT ) { const from_value = value; const from_obj_type = value_type as ObjectType; // check value_type's shape let from_shape = value.shape; if (!from_shape) { // value has no shape cast to new shaped value // try get from object const meta = from_obj_type.meta; from_shape = meta.originShape; // get the originShape } const to_meta = (type as ObjectType).meta; let to_shape = to_meta.originShape; /* to_meta may differ from from_meta, like undefined interface NoddOptions { attrs?: number } const opts: NoddOptions = {} */ if ( from_value instanceof NewLiteralObjectValue && to_meta.members.length > from_obj_type.meta.members.length ) { /* reorder the order of r-value initialization values according to the shape of the l-value * * e.g. * interface Node { * x?: number; * y?: string; * z?: boolean; * } * const n: Node = {z: true, x: 10}; * * reorder initValues from '{z: true, x: 10}' to '{x: 10, y: undefined, z: true}' */ const initValues: SemanticsValue[] = []; for (const to_member of to_meta.members) { if ( from_obj_type.meta.members.find((from_member) => { return from_member.name === to_member.name; }) ) { const from_member = from_obj_type.meta.findMember( to_member.name, )!; const v = (from_value as NewLiteralObjectValue).initValues[ from_member.index ]; if (!from_member.valueType.equals(to_member.valueType)) { initValues.push(newCastValue(to_member.valueType, v)); } else { initValues.push(v); } continue; } if ( to_member.valueType.kind === ValueTypeKind.UNION && (to_member.valueType).types.has( Primitive.Undefined, ) ) { initValues.push( new LiteralValue(Primitive.Undefined, undefined), ); } } // when the 'meta' of r-value is modified, the 'typeId' of r-value also needs to be modified. from_value.initValues = initValues; (from_value.type as ObjectType).meta.members = to_meta.members; from_value.type.typeId = type.typeId; } if (from_shape && from_shape.isStaticShape()) { to_shape = to_meta.buildShape(from_shape); } const cast_value = new CastValue( SemanticsValueKind.OBJECT_CAST_OBJECT, type, from_value, ); cast_value.shape = to_shape; return cast_value; } if (isObjectType(type.kind) && value_type.kind == ValueTypeKind.ANY) { return new CastValue(SemanticsValueKind.ANY_CAST_OBJECT, type, value); } if ( (value_type.kind === ValueTypeKind.NULL && type.kind === ValueTypeKind.OBJECT) || type.kind === ValueTypeKind.FUNCTION ) { /* null to object don't require cast */ return value; } if ( type.kind === ValueTypeKind.GENERIC && value_type.kind !== ValueTypeKind.GENERIC ) { /* no cast is required from other types to generic type */ return value; } throw Error(`cannot make cast value from "${value_type}" to "${type}"`); } function typeUp(upValue: SemanticsValue, downValue: SemanticsValue): boolean { const up = upValue.type; const down = downValue.type; if (down.kind == ValueTypeKind.ANY) return true; if (up.kind == ValueTypeKind.NUMBER) { if ( down.kind === ValueTypeKind.WASM_F32 || down.kind == ValueTypeKind.WASM_I64 || down.kind == ValueTypeKind.INT || down.kind === ValueTypeKind.BOOLEAN ) { return true; } } if (up.kind === ValueTypeKind.INT) { if (down.kind == ValueTypeKind.BOOLEAN) { return true; } if ( downValue instanceof LiteralValue && typeof downValue.value === 'number' ) { return judgeIsInt(downValue.value as number); } // TODO: check if upValue's value is integer, if true, return true } if (up.kind === ValueTypeKind.WASM_I64) { if ( down.kind == ValueTypeKind.BOOLEAN || down.kind == ValueTypeKind.INT ) { return true; } if ( downValue instanceof LiteralValue && typeof downValue.value === 'number' && (downValue.value as number) % 1 === 0 ) { return true; } // TODO: check if upValue's value is integer, if true, return true } if (up.kind == ValueTypeKind.WASM_F32) { if ( down.kind == ValueTypeKind.BOOLEAN || down.kind == ValueTypeKind.WASM_I64 || down.kind == ValueTypeKind.INT ) { return true; } } if (up.kind == ValueTypeKind.STRING || up.kind == ValueTypeKind.RAW_STRING) return true; if ( up.kind == ValueTypeKind.OBJECT && (down.kind == ValueTypeKind.UNDEFINED || down.kind == ValueTypeKind.NULL) ) { return true; } return false; } function needTranslated(value1: SemanticsValue, value2: SemanticsValue) { const type1 = value1.effectType; const type2 = value2.effectType; if (type1.kind === ValueTypeKind.ANY || type2.kind === ValueTypeKind.ANY) { return false; } if (type1.isWASM || type2.isWASM) { return true; } return false; } function typeTranslate( value1: SemanticsValue, value2: SemanticsValue, ): ValueType { const type1 = value1.effectType; const type2 = value2.effectType; if (type1.equals(type2)) return type1; if (typeUp(value1, value2)) return type1; if (typeUp(value2, value1)) return type2; throw Error(`"${type1}" aginst of "${type2}"`); } export function shapeAssignCheck(left: ValueType, right: ValueType): boolean { // iff the type of lvalue is 'any', we should never fix its shape. if (left.equals(Primitive.Any)) return false; /* e.g. * interface I { * x: string[] * } * * const i: I = { x: [] } */ if (left instanceof ArrayType && right instanceof ArrayType) { if ( left.element.kind !== ValueTypeKind.ANY && right.element.kind == ValueTypeKind.ANY ) return false; } if ( left.kind == ValueTypeKind.OBJECT && right.kind == ValueTypeKind.OBJECT ) { if (left.equals(right)) return true; const leftMeta = (left as ObjectType).meta; const rightMeta = (right as ObjectType).meta; if (rightMeta.members.length >= leftMeta.members.length) { for (const left_member of leftMeta.members) { const right_member = rightMeta.findMember(left_member.name); /* e.g. interface I { x?: number; } class A { y = 10; z = true; } const a = new A(); const i: I = a; */ if (!right_member) return false; // Property 'x' is missing in type 'A' but required in type 'I'. /* e.g. interface I { x?: number; } class A { x = 10; y = true; } const a = new A(); const i: I = a; */ if (left_member.isOptional && !right_member.isOptional) return false; if ( left_member.valueType instanceof UnionType && !(right_member.valueType instanceof UnionType) ) { return false; } if ( left_member.valueType instanceof ObjectType && right_member.valueType instanceof ObjectType ) { return shapeAssignCheck( left_member.valueType, right_member.valueType, ); } } } else { /* e.g. interface I { x: number; y?: boolean; } class A { x = 10; } const a = new A(); const i: I = a; */ return false; // property 'y' is missing in type 'A' but required in type 'I'. } } return true; } export function newBinaryExprValue( type: ValueType | undefined, opKind: ValueBinaryOperator, left_value: SemanticsValue, right_value: SemanticsValue, ): SemanticsValue { const is_equal = isEqualOperator(opKind); Logger.debug( `=== newBinaryExprValue left_value type ${left_value.effectType}`, ); if ( is_equal && shapeAssignCheck(left_value.effectType, right_value.effectType) ) left_value.shape = right_value.shape; if (isCompareOperator(opKind)) { /** Adding instanceof to the comparison operator can result in type coercion to any, * which prevents compile-time verification of the instanceof relationship. */ /* TSC can guarantee that the types must be the same when both sides are primitive types. And if the type of lvalue and rvalue are both primitive types, there is no need to convert the type of lvalue and the type of rvalue to "any". */ if (left_value.type.kind == ValueTypeKind.ENUM) { const enum_type = left_value.type as EnumType; left_value = newCastValue(enum_type.memberType, left_value); } if (right_value.type.kind == ValueTypeKind.ENUM) { const enum_type = right_value.type as EnumType; right_value = newCastValue(enum_type.memberType, right_value); } if ( !( (left_value.type.isPrimitive || left_value.type.isWASM) && (right_value.type.isPrimitive || right_value.type.isWASM) ) ) { left_value = newCastValue(Primitive.Any, left_value); right_value = newCastValue(Primitive.Any, right_value); } else { if (needTranslated(left_value, right_value)) { const target_type = typeTranslate(left_value, right_value); if (!target_type.equals(left_value.effectType)) left_value = newCastValue(target_type, left_value); if (!target_type.equals(right_value.effectType)) right_value = newCastValue(target_type, right_value); } } } else if ( left_value.type.isSpecialized() && !right_value.type.isSpecialized() && left_value.type.genericOwner!.equals(right_value.type) ) { //e.g const a : number[] = new Array(); right_value.type = left_value.type; if (is_equal && isMemberSetValue(left_value)) { return updateSetValue(left_value, right_value, opKind); } } else if (!left_value.effectType.equals(right_value.effectType)) { if (is_equal) { if ( left_value.effectType instanceof ObjectType && right_value instanceof NewLiteralObjectValue ) { const l_meta = left_value.effectType.meta; const r_meta = right_value.objectType.meta; for ( let index = 0; index < right_value.initValues.length; index++ ) { const v = right_value.initValues[index]; if (v instanceof NewArrayLenValue) { const r_member = r_meta.members[index]; const l_member = l_meta.findMember(r_member.name); if ( l_member && l_member.valueType.kind == ValueTypeKind.ARRAY ) { v.type = l_member.valueType; r_member.valueType = l_member.valueType; } } } } if ( right_value instanceof NewArrayLenValue && left_value.type.kind === ValueTypeKind.ARRAY ) { /* For NewArrayLenValue with zero length, update the array type according to the assign target */ right_value.type = left_value.type; } else if ( left_value.effectType instanceof WASMArrayType && right_value.type instanceof ArrayType ) { /* In this situation, we want to create a raw wasm array, no need to cast */ } else { right_value = newCastValue(left_value.effectType, right_value); if (isMemberSetValue(left_value)) { return updateSetValue(left_value, right_value, opKind); } } } else if (opKind !== ts.SyntaxKind.InstanceOfKeyword) { const target_type = typeTranslate(left_value, right_value); if (!target_type.equals(left_value.effectType)) left_value = newCastValue(target_type, left_value); if (!target_type.equals(right_value.effectType)) right_value = newCastValue(target_type, right_value); } } else { if (is_equal && isMemberSetValue(left_value)) { return updateSetValue(left_value, right_value, opKind); } } let result_type = type ?? left_value.effectType; if (isCompareOperator(opKind)) { result_type = Primitive.Boolean; } if (opKind === ts.SyntaxKind.InstanceOfKeyword) { result_type = Primitive.Boolean; } const bin_value = new BinaryExprValue( result_type, opKind, left_value, right_value, ); bin_value.shape = left_value.shape; return bin_value; } function isMemberSetValue(v: SemanticsValue): boolean { return ( v.kind == SemanticsValueKind.DYNAMIC_SET || v.kind == SemanticsValueKind.OFFSET_SET || v.kind == SemanticsValueKind.OFFSET_SETTER || v.kind == SemanticsValueKind.SHAPE_SET || v.kind == SemanticsValueKind.DIRECT_SETTER || v.kind == SemanticsValueKind.VTABLE_SET || v.kind == SemanticsValueKind.STRING_INDEX_SET || v.kind == SemanticsValueKind.ARRAY_INDEX_SET || v.kind == SemanticsValueKind.OBJECT_INDEX_SET || v.kind == SemanticsValueKind.OBJECT_KEY_SET || v.kind == SemanticsValueKind.WASMARRAY_INDEX_SET || v.kind == SemanticsValueKind.WASMSTRUCT_INDEX_SET ); } function isMemberCallValue(kind: SemanticsValueKind): boolean { return ( kind == SemanticsValueKind.DYNAMIC_CALL || kind == SemanticsValueKind.SHAPE_CALL || kind == SemanticsValueKind.VTABLE_CALL || kind == SemanticsValueKind.OFFSET_CALL || kind == SemanticsValueKind.DIRECT_CALL ); } function isMemberGetValue(kind: SemanticsValueKind): boolean { return ( kind == SemanticsValueKind.DYNAMIC_GET || kind == SemanticsValueKind.SHAPE_GET || kind == SemanticsValueKind.VTABLE_GET || kind == SemanticsValueKind.OFFSET_GET || kind == SemanticsValueKind.OFFSET_GETTER || kind == SemanticsValueKind.DIRECT_GETTER ); } function updateSetValue( left: SemanticsValue, right: SemanticsValue, op: ValueBinaryOperator, ): SemanticsValue { if (left instanceof ElementSetValue) { const es = left as ElementSetValue; es.value = right; es.opKind = op; } else { const ps = left as MemberSetValue; ps.value = right; ps.opKind = op; } return left; } function buildBinaryExpression( expr: BinaryExpression, context: BuildContext, ): SemanticsValue { const is_equal = isEqualOperator(expr.operatorKind); context.pushReference( is_equal ? ValueReferenceKind.LEFT : ValueReferenceKind.RIGHT, ); const left_value = buildExpression(expr.leftOperand, context); left_value.incAccessCount(); context.popReference(); /* FIX ME: This code is a workaround!!! In order to make the test case [array_every_interface] in array_every.ts pass, we add this code. Need to find a solution from the root. */ const rightOperand = expr.rightOperand; if (rightOperand instanceof ArrayLiteralExpression) { const arrayLiteralExpression = rightOperand as ArrayLiteralExpression; if (arrayLiteralExpression.exprType instanceof TSArray) { const arr_type = arrayLiteralExpression.exprType as TSArray; const element_type = arr_type.elementType; if (element_type instanceof TSClass) { const class_type = element_type as TSClass; if (class_type.isLiteral) { if (expr.leftOperand instanceof PropertyAccessExpression) { const left = expr.leftOperand as PropertyAccessExpression; const own = left.propertyAccessExpr as IdentifierExpression; const property = left.propertyExpr as IdentifierExpression; const own_type = property.exprType; rightOperand.setExprType(own_type); } } } } } context.pushReference(ValueReferenceKind.RIGHT); let operatorKind = expr.operatorKind; const flatten_opKind = flattenOperator(expr.operatorKind); const isCompoundAssignmentOp = !(flatten_opKind === expr.operatorKind); let right_value = buildExpression(expr.rightOperand, context); if (isCompoundAssignmentOp) { right_value = newBinaryExprValue( undefined, flatten_opKind as ValueBinaryOperator, buildExpression(expr.leftOperand, context), right_value, ); operatorKind = ts.SyntaxKind.EqualsToken; } right_value.incAccessCount(); context.popReference(); if (is_equal && expr.leftOperand instanceof PropertyAccessExpression) { const properexpr = expr.leftOperand as PropertyAccessExpression; if ( properexpr.propertyAccessExpr.exprType.kind == TypeKind.ARRAY && properexpr.propertyExpr instanceof IdentifierExpression && properexpr.propertyExpr.identifierName == 'length' ) { left_value.type = Primitive.Int; right_value.type = Primitive.Int; } } return newBinaryExprValue( undefined, operatorKind as ValueBinaryOperator, left_value, right_value, ); } function buildCommaExpression( commaExpr: CommaExpression, context: BuildContext, ): SemanticsValue { const exprs: SemanticsValue[] = []; for (const e of commaExpr.exprs) { context.pushReference(ValueReferenceKind.RIGHT); exprs.push(buildExpression(e, context)); context.popReference(); } const type = context.findValueType(commaExpr.exprType)!; return new CommaExprValue(type, exprs); } function buildConditionalExpression( expr: ConditionalExpression, context: BuildContext, ): SemanticsValue { context.pushReference(ValueReferenceKind.RIGHT); let condition = buildExpression(expr.condtion, context); condition = newCastValue(Primitive.Boolean, condition); context.popReference(); context.pushReference(ValueReferenceKind.RIGHT); const true_value = buildExpression(expr.whenTrue, context); true_value.incAccessCount(); context.popReference(); context.pushReference(ValueReferenceKind.RIGHT); const false_value = buildExpression(expr.whenFalse, context); false_value.incAccessCount(); context.popReference(); let result_type = true_value.type; if (!result_type.equals(false_value.type)) { result_type = createUnionType( context, new Set([true_value.type, false_value.type]), ); } const conditon_expr = new ConditionExprValue( result_type, condition, true_value, false_value, ); return conditon_expr; // return flattenConditionValue(conditon_expr); } function getClassContructorType(obj_type: ObjectType): SemanticsValue { if (!obj_type.isClassObject()) { throw Error(`ObjectType is not a class ${obj_type}`); } const meta = obj_type.meta; const ctr = meta.findConstructor(); if ( !ctr || (ctr.type != MemberType.METHOD && ctr.type != MemberType.CONSTRUCTOR) ) { throw Error(`ObjectType is not a constructor ${obj_type}`); } const shape = meta.originShape; const shape_member = shape!.getMember(ctr.index); if (!shape_member) { throw Error(`ObjectType constructor must exist: ${obj_type}`); } return (shape_member as ShapeMethod).method! as SemanticsValue; } function createMemberCallValue( getter: MemberGetValue, funcType: FunctionType, ): MemberCallValue | undefined { switch (getter.kind) { case SemanticsValueKind.DYNAMIC_GET: return new DynamicCallValue( getter.owner, (getter as DynamicGetValue).name, ); case SemanticsValueKind.SHAPE_GET: return new ShapeCallValue( getter.owner, funcType, (getter as ShapeGetValue).index, ); case SemanticsValueKind.VTABLE_GET: return new VTableCallValue( getter.owner, funcType, (getter as VTableGetValue).index, ); case SemanticsValueKind.OFFSET_GET: return new OffsetCallValue( getter.owner, funcType, (getter as OffsetGetValue).index, ); } return undefined; } function buildParameters( context: BuildContext, args: Expression[], func_type?: FunctionType, ): SemanticsValue[] { const params: SemanticsValue[] = []; for (let i = 0; i < args.length; i++) { const arg = args[i]; let arg_type = Primitive.Any; if (func_type) { arg_type = func_type.getParamType(i) ?? Primitive.Any; } context.pushReference(ValueReferenceKind.RIGHT); const arg_value = buildExpression(arg, context); arg_value.incAccessCount(); const value = newCastValue(arg_type, arg_value); context.popReference(); params.push(value); } return params; } function buildTypeArguments(context: BuildContext, types: Type[]): ValueType[] { const typeArgs: ValueType[] = []; for (const t of types) { const vt = context.findValueTypeByKey(t); if (!vt) { throw Error(`buildTypeArguments faield: from ${t}`); } if (vt instanceof ObjectType) { typeArgs.push(vt.instanceType!); } typeArgs.push(vt); } return typeArgs; } function updateValueTypeByTypeArguments( valueType: ValueTypeWithArguments, typeArgs: ValueType[], ): ValueType { if (valueType.typeArguments) { const mapper = new SpecializeTypeMapper(valueType, typeArgs); return mapper.getValueType(valueType); } return valueType; } class GuessTypeArguments { typeArgs: ValueType[]; constructor(public typeArguments: TypeParameterType[]) { this.typeArgs = new Array(typeArguments.length); } guessType(templateType: ValueType, valueType: ValueType) { if (templateType.kind == ValueTypeKind.TYPE_PARAMETER) { this.updateTypeMap(templateType as TypeParameterType, valueType); return; } if (templateType.kind == ValueTypeKind.UNION) { const unionType = templateType as UnionType; unionType.types.forEach((t) => { if (t.kind == ValueTypeKind.TYPE_PARAMETER) { this.updateTypeMap(t as TypeParameterType, valueType); } }); return; } if (valueType.kind != templateType.kind) { throw Error( `Cannot guess the value type: template: ${templateType}, valueType: ${valueType}`, ); } switch (valueType.kind) { case ValueTypeKind.OBJECT: case ValueTypeKind.ARRAY: this.guessObjectType( templateType as ObjectType, valueType as ObjectType, ); break; case ValueTypeKind.FUNCTION: this.guessFunctionType( templateType as FunctionType, valueType as FunctionType, ); break; } } updateTypeMap(typeType: TypeParameterType, valueType: ValueType) { const idx = this.typeArguments.indexOf(typeType); if (idx >= 0 && !this.typeArgs[idx]) this.typeArgs[idx] = valueType; } guessObjectType(templateObjType: ObjectType, objType: ObjectType) { if ( objType.genericOwner === templateObjType && templateObjType.typeArguments && objType.specialTypeArguments ) { for ( let i = 0; i < templateObjType.typeArguments.length && i < objType.specialTypeArguments.length; i++ ) { this.guessType( templateObjType.typeArguments[i], objType.specialTypeArguments[i], ); } } } guessFunctionType(templateFuncType: FunctionType, valueType: FunctionType) { // TODO Remove CLOSURECONTEXT && EMPTY to function type let arg_idx = 0; let temp_idx = 0; while ( arg_idx < valueType.argumentsType.length && temp_idx < templateFuncType.argumentsType.length ) { const arg_type = valueType.argumentsType[arg_idx]; if ( arg_type.kind == ValueTypeKind.CLOSURECONTEXT || arg_type.kind == ValueTypeKind.EMPTY ) { arg_idx++; continue; } const temp_type = templateFuncType.argumentsType[temp_idx]; if ( temp_type.kind == ValueTypeKind.CLOSURECONTEXT || temp_type.kind == ValueTypeKind.EMPTY ) { temp_idx++; continue; } this.guessType(temp_type, arg_type); arg_idx++; temp_idx++; } this.guessType(templateFuncType.returnType, valueType.returnType); } } function specializeFuncTypeArgumentsByArgs( context: BuildContext, func_type: FunctionType, args: Expression[], ret_type: Type, ): ValueType[] { const guess = new GuessTypeArguments(func_type.typeArguments!); // TODO Remove CLOSURECONTEXT && EMPTY to function type let arg_idx = 0; let temp_idx = 0; while (temp_idx < func_type.argumentsType.length && arg_idx < args.length) { const temp_type = func_type.getParamType(temp_idx)!; if ( temp_type.kind == ValueTypeKind.CLOSURECONTEXT || temp_type.kind == ValueTypeKind.EMPTY ) { temp_idx++; continue; } const arg_type = context.findValueTypeByKey(args[arg_idx].exprType)!; if ( arg_type.kind == ValueTypeKind.CLOSURECONTEXT || arg_type.kind == ValueTypeKind.EMPTY ) { arg_idx++; continue; } guess.guessType(temp_type, arg_type); temp_idx++; arg_idx++; } guess.guessType( func_type.returnType, context.findValueTypeByKey(ret_type)!, ); return guess.typeArgs; } function buildCallExpression( expr: CallExpression, context: BuildContext, ): SemanticsValue { context.pushReference(ValueReferenceKind.RIGHT); let func = buildExpression(expr.callExpr, context); context.popReference(); if (func instanceof ToStringValue) { return func; } if (func.kind === SemanticsValueKind.DYNAMIC_GET) { const p = func as MemberGetValue; if ( p.type.kind != ValueTypeKind.FUNCTION && p.type.kind != ValueTypeKind.ANY ) { throw Error(`Property Access Result is not a function`); } const f_type = p.type.kind == ValueTypeKind.ANY ? (GetPredefinedType( PredefinedTypeId.FUNC_ANY_ARRAY_ANY_DEFAULT, ) as FunctionType) : (p.type as FunctionType); const new_func = createMemberCallValue(p, f_type); if (new_func) func = new_func; /* Workaround: console.log's type is wrong */ //(func as MemberCallValue).funcType = f_type; } else if (func.kind == SemanticsValueKind.SUPER) { // create the super call const super_value = func as SuperValue; const ctr = getClassContructorType(super_value.type as ObjectType); func = new ConstructorCallValue( super_value, ctr, ctr.type as FunctionType, ); } else if (func.type.kind == ValueTypeKind.FUNCTION) { if (func instanceof VarValue) { const f_type = func.type as FunctionType; if (func.ref instanceof FunctionDeclareNode) { func = new FunctionCallValue(f_type.returnType, func, f_type); } else if (func.ref instanceof VarDeclareNode) { func = new ClosureCallValue(f_type.returnType, func, f_type); } } else if (func instanceof OffsetGetValue) { const f_type = func.type as FunctionType; func = new ClosureCallValue(f_type.returnType, func, f_type); } else { // We have already created call node during processing PropertyAccessExpression if ( expr.callExpr.expressionKind !== ts.SyntaxKind.PropertyAccessExpression ) { const f_type = func.type as FunctionType; func = new ClosureCallValue(f_type.returnType, func, f_type); } } } else if ( func instanceof VarValue && (func.type.kind === ValueTypeKind.ANY || func.type.kind === ValueTypeKind.UNION) ) { /* any value's functype can not be ensure, return value type will always be any */ let parameters: SemanticsValue[] | undefined = undefined; if (expr.callArgs.length > 0) { parameters = buildParameters(context, expr.callArgs); } return new AnyCallValue(Primitive.Any, func, parameters); } else if (!isMemberCallValue(func.kind)) { throw Error( `unkown type for function call func kind: ${ SemanticsValueKind[func.kind] }`, ); } let func_type = (func as FunctionCallBaseValue).funcType; if (expr.typeArguments || func_type.typeArguments) { let specialTypeArgs: ValueType[]; if (expr.typeArguments) { specialTypeArgs = buildTypeArguments(context, expr.typeArguments!); } else { // specialize the typeArguments by callArgs and return type specialTypeArgs = specializeFuncTypeArgumentsByArgs( context, func_type, expr.callArgs, expr.exprType, ); } func_type = updateValueTypeByTypeArguments( func_type, specialTypeArgs, ) as FunctionType; (func as FunctionCallBaseValue).funcType = func_type; } // process the arguments if (expr.callArgs.length > 0) { (func as FunctionCallBaseValue).parameters = buildParameters( context, expr.callArgs, func_type, ); } context.popReference(); if (!func.shape) { func.shape = GetShapeFromType(func_type.returnType); } func.type = func_type.returnType; // reset the func type func.shape = GetShapeFromType(func.type); return func; } function buildEnumerateKeysExpr( expr: EnumerateKeysExpression, context: BuildContext, ) { const valueType = context.findValueTypeByKey(expr.exprType)!; context.pushReference(ValueReferenceKind.RIGHT); const obj = buildExpression(expr.targetObj, context); context.popReference(); return new EnumerateKeysGetValue(valueType, obj); } function buildNewExpression2( expr: NewExpression, context: BuildContext, ): SemanticsValue { context.pushReference(ValueReferenceKind.RIGHT); let class_value = buildExpression(expr.newExpr, context); context.popReference(); const type = class_value.effectType; if (!(type instanceof ObjectType)) { throw Error(`${class_value} is not a class or class interface`); } let valueTypeArgs: ValueType[] | undefined = undefined; if (expr.typeArguments) { valueTypeArgs = buildTypeArguments(context, expr.typeArguments); } let object_type = type as ObjectType; if (object_type.instanceType) object_type = object_type.instanceType; if (expr.typeArguments) { // handle array specialization if (object_type.kind == ValueTypeKind.ARRAY) { object_type = updateValueTypeByTypeArguments( object_type, valueTypeArgs!, ) as ObjectType; } else { const exprObjType = context.module.findValueTypeByType( expr.exprType, )! as ObjectType; if ( exprObjType && exprObjType.genericOwner && exprObjType.genericType.equals(object_type) ) { class_value = context.getGlobalValue(exprObjType.meta.name)!; object_type = exprObjType; } } } const clazz_type = object_type.classType; let obj_type: WASMArrayType | ArrayType; if (expr.exprType instanceof WasmArrayType) { obj_type = createType(context, expr.exprType) as WASMArrayType; } else { obj_type = object_type as ArrayType; } if (clazz_type && clazz_type.kind == ValueTypeKind.ARRAY) { if (expr.lenExpr != null) { const lenExpr = buildExpression(expr.lenExpr!, context); const object_value = new NewArrayLenValue(obj_type, lenExpr); if (valueTypeArgs) (object_value).setTypeArguments( valueTypeArgs, ); return object_value; } else { return buildNewArrayParameters( context, obj_type, expr.newArgs, valueTypeArgs, ); } } if (clazz_type && clazz_type.isClassObject()) { return buildNewClass( context, class_value, clazz_type, expr.newArgs, valueTypeArgs, ); } else if (!clazz_type && object_type.isObject()) { // new interface_value return buildNewInterface( context, class_value, object_type, expr.newArgs, valueTypeArgs, ); } throw Error(`${class_value} is not a class or class interface`); } function buildNewClass( context: BuildContext, class_value: SemanticsValue, object_type: ObjectType, params: Expression[] | undefined, valueTypeArgs: ValueType[] | undefined, ): SemanticsValue { if (!object_type.isClassObject()) { throw Error(`${class_value} shoubld be a class: ${object_type}`); } let param_values: SemanticsValue[] = []; if (params && params.length > 0) { const class_meta = object_type.meta; const member_ctr = class_meta.findConstructor(); if (!member_ctr) { throw Error(`${object_type} cannot found constructor`); } const value_type = member_ctr!.valueType; const func_type = value_type.kind == ValueTypeKind.FUNCTION ? (value_type as FunctionType) : undefined; param_values = buildParameters(context, params, func_type); } const object_value = new NewConstructorObjectValue( object_type.instanceType!, //class_value, param_values, ); if (valueTypeArgs) object_value.setTypeArguments(valueTypeArgs); return object_value; } function buildNewArrayParameters( context: BuildContext, arr_type: ArrayType | WASMArrayType, params: Expression[] | undefined, valueTypeArgs: ValueType[] | undefined, ): SemanticsValue { const param_values: SemanticsValue[] = []; let init_types: Set | undefined = undefined; if (!arr_type.isSpecialized() && params && params.length > 0) { init_types = new Set(); } if (params && params && params.length > 0) { for (const p of params) { context.pushReference(ValueReferenceKind.RIGHT); let v = buildExpression(p, context); context.popReference(); const elem_type = arr_type instanceof ArrayType ? arr_type.element : (arr_type).arrayType.element; if (v.type !== elem_type) { v = newCastValue(elem_type, v); } param_values.push(v); if (init_types) { init_types.add(v.type); } } } let arr_value: SemanticsValue; if (arr_type instanceof ArrayType) { if (init_types) { arr_type = createArrayType( context, CreateWideTypeFromTypes(context, init_types), ); } if (!arr_type.isSpecialized()) { arr_type = GetPredefinedType( PredefinedTypeId.ARRAY_ANY, )! as ArrayType; } arr_value = new NewArrayValue( (arr_type).instanceType! as ArrayType, param_values, ); if (valueTypeArgs) { (arr_value).setTypeArguments(valueTypeArgs); } } else { arr_value = new NewArrayValue(arr_type as WASMArrayType, param_values); } return arr_value; } function buildNewInterface( context: BuildContext, class_value: SemanticsValue, object_type: ObjectType, params: Expression[] | undefined, valueTypeArgs: ValueType[] | undefined, ): SemanticsValue { const meta = object_type.meta; const ctr_member = meta.findConstructor(); if (!ctr_member) { throw Error(`${object_type} does not have constructor`); } const func_type = ctr_member.valueType as FunctionType; const target_type = func_type.returnType; if (!target_type) { throw Error( `class_value: ${class_value} constructor: ${ctr_member} should return an object type`, ); } if ( target_type.kind != ValueTypeKind.OBJECT && target_type.kind != ValueTypeKind.TYPE_PARAMETER ) { throw Error(`${object_type} should return an object ${func_type}`); } let param_values: SemanticsValue[] = []; if (params && params.length > 0) { param_values = buildParameters(context, params, func_type); } const obj_value = new NewFromClassObjectValue( target_type as ObjectType, class_value, param_values, ); if (valueTypeArgs) obj_value.setTypeArguments(valueTypeArgs); return obj_value; } function buildAsExpression( expr: AsExpression, context: BuildContext, ): SemanticsValue { context.pushReference(ValueReferenceKind.RIGHT); const right = buildExpression(expr.expression, context); context.popReference(); // TODO process the template type parameter const value_type = context.module.findValueTypeByType(expr.exprType); if (!value_type) { throw Error( `Unknow Type for AsExpression: ${SymbolKeyToString(expr.exprType)}`, ); } return newCastValue(value_type, right); } function buildElementAccessExpression( expr: ElementAccessExpression, context: BuildContext, ): SemanticsValue { context.pushReference(ValueReferenceKind.RIGHT); const own = buildExpression(expr.accessExpr, context); let arg = buildExpression(expr.argExpr, context); context.popReference(); const is_index = arg.type.kind == ValueTypeKind.NUMBER || arg.type.kind == ValueTypeKind.INT; const ref_type = context.currentReference(); const is_set = ref_type == ValueReferenceKind.LEFT; let element_type = is_set ? SemanticsValueKind.OBJECT_KEY_SET : SemanticsValueKind.OBJECT_KEY_GET; const type = own.effectType; let value_type = Primitive.Any; if (is_index) { if ( type.kind == ValueTypeKind.STRING || type.kind == ValueTypeKind.RAW_STRING ) { element_type = is_set ? SemanticsValueKind.STRING_INDEX_SET : SemanticsValueKind.STRING_INDEX_GET; value_type = Primitive.String; } else if (type.kind == ValueTypeKind.ARRAY) { element_type = is_set ? SemanticsValueKind.ARRAY_INDEX_SET : SemanticsValueKind.ARRAY_INDEX_GET; const array_type = type as ArrayType; value_type = array_type.element; } else if (type.kind == ValueTypeKind.OBJECT) { const obj_type = type as ObjectType; if (obj_type.numberIndexType) { value_type = obj_type.numberIndexType; element_type = is_set ? SemanticsValueKind.OBJECT_INDEX_SET : SemanticsValueKind.OBJECT_INDEX_GET; } } else if (type.kind === ValueTypeKind.TUPLE) { element_type = is_set ? SemanticsValueKind.TUPLE_INDEX_SET : SemanticsValueKind.TUPLE_INDEX_GET; if (arg instanceof LiteralValue) { const index = arg.value as number; value_type = (type as TupleType).elements[index]; } } else if (type.kind === ValueTypeKind.WASM_ARRAY) { element_type = is_set ? SemanticsValueKind.WASMARRAY_INDEX_SET : SemanticsValueKind.WASMARRAY_INDEX_GET; value_type = (type as WASMArrayType).arrayType.element; } else if (type.kind === ValueTypeKind.WASM_STRUCT) { element_type = is_set ? SemanticsValueKind.WASMSTRUCT_INDEX_SET : SemanticsValueKind.WASMSTRUCT_INDEX_GET; if (arg instanceof LiteralValue) { const index = arg.value as number; value_type = (type as WASMStructType).tupleType.elements[index]; } } } else { if (type.kind == ValueTypeKind.OBJECT) { const obj_type = type as ObjectType; if (obj_type.stringIndexType) { value_type = obj_type.stringIndexType; } else { value_type = createType(context, expr.exprType); } } } if (type.kind == ValueTypeKind.ENUM) { arg = newCastValue((type as EnumType).memberType, arg); return new ElementGetValue( SemanticsValueKind.ENUM_KEY_GET, Primitive.String, own, arg, ); } if (is_set) { return new ElementSetValue( element_type as ElementSetValueKind, value_type, own, arg, ); } else { return new ElementGetValue( element_type as ElementGetValueKind, value_type, own, arg, ); } } function getCurrentClassType(context: BuildContext): TSClass | undefined { let scope: Scope | null = context.top().scope; while (scope != null && scope.kind != ScopeKind.ClassScope) { scope = scope.parent ? scope.parent : null; } if (scope) return (scope as ClassScope).classType; return undefined; } function buildThisValue2(context: BuildContext): SemanticsValue { const type = getCurrentClassType(context); if (!type) { throw Error(`cannot find the this type`); } const obj_type = context.findValueType(type)! as ObjectType; const this_value = new ThisValue2(obj_type); return this_value; } function buildSuperValue( expr: SuperExpression, context: BuildContext, ): SemanticsValue { const clazz = getCurrentClassType(context); let param_values: SemanticsValue[] = []; if (!clazz) { throw Error('cannot find the current class type for super'); } const super_class = clazz!.getBase(); if (!super_class) { throw Error(`class "${clazz.mangledName}" has no super type`); } const super_type = context.findValueType(super_class!)! as ObjectType; const ts_node = expr.tsNode; if (ts_node == undefined) { throw Error(`cannot find the context for using super`); } const context_kind = ts_node.parent.kind; let isStaticFunc = false; if (context_kind == ts.SyntaxKind.PropertyAccessExpression) { const propertyName = ( (ts_node.parent as ts.PropertyAccessExpression) .name as ts.Identifier ).escapedText; super_class.memberFuncs.forEach((func) => { if (func.name == propertyName) { isStaticFunc = func.type.isStatic; } }); if (!isStaticFunc) { return new SuperValue(super_type, SuperUsageFlag.SUPER_LITERAL); } else { return context.findSymbolKey(super_class) as VarValue; } } else { if (expr.callArgs && expr.callArgs.length > 0) { const ctorType = super_type.meta.ctor!.valueType; param_values = buildParameters( context, expr.callArgs, ctorType as FunctionType, ); } return new SuperValue( super_type, SuperUsageFlag.SUPER_CALL, param_values, ); } } function buildSuperExpression( expr: SuperExpression, context: BuildContext, ): SemanticsValue { return buildSuperValue(expr, context); } function buildUnaryExpression( expr: UnaryExpression, context: BuildContext, ): SemanticsValue { context.pushReference(ValueReferenceKind.RIGHT); const operand = buildExpression(expr.operand, context); context.popReference(); let flattenExprValue: SemanticsValue | undefined; switch (expr.operatorKind) { case ts.SyntaxKind.PlusPlusToken: case ts.SyntaxKind.MinusMinusToken: { /* i++ ===> i += 1 */ /* i-- ===> i -= 1 */ const tmpOpKind = flattenOperator(expr.operatorKind); const tmpLiteralExpression = new NumberLiteralExpression(1); const tmpBinaryExpression = new BinaryExpression( tmpOpKind, expr.operand, tmpLiteralExpression, ); flattenExprValue = buildBinaryExpression( tmpBinaryExpression, context, ); break; } case ts.SyntaxKind.MinusToken: { /* -8 ==> 0-8, -a ===> 0-a */ const tmpOpKind = flattenOperator(expr.operatorKind); const tmpLiteralExpression = new NumberLiteralExpression(0); const tmpBinaryExpression = new BinaryExpression( tmpOpKind, tmpLiteralExpression, expr.operand, ); flattenExprValue = buildBinaryExpression( tmpBinaryExpression, context, ); break; } } if (expr.expressionKind == ts.SyntaxKind.PrefixUnaryExpression) return new PrefixUnaryExprValue( operand.type as PrimitiveType, expr.operatorKind as ts.PrefixUnaryOperator, operand, flattenExprValue, ); return new PostUnaryExprValue( operand.type as PrimitiveType, expr.operatorKind as ts.PostfixUnaryOperator, operand, flattenExprValue, ); } export function buildFunctionExpression( funcScope: FunctionScope, context: BuildContext, ): SemanticsValue { const func = context.globalSymbols.get(funcScope); if ( !func || (!( func instanceof SemanticsNode && (func! as SemanticsNode).kind == SemanticsKind.FUNCTION ) && !( func instanceof SemanticsValue && (func! as SemanticsValue).type.kind == ValueTypeKind.FUNCTION )) ) { throw Error(`Cannot found the function ${funcScope.funcName}`); } let func_node: FunctionDeclareNode | undefined = undefined; if ((func as unknown) instanceof SemanticsValue) { /* const func = function() { ... } */ /* function is anonymous */ const value = func as SemanticsValue; if ( value instanceof VarValue && (value as VarValue).ref instanceof FunctionDeclareNode ) { func_node = (value as VarValue).ref as FunctionDeclareNode; } } else { func_node = func as SemanticsNode as FunctionDeclareNode; } if (!func_node) { throw Error( `Cannot get the right function declearation from functionscope ${funcScope.funcName}, function: ${func}`, ); } // new Closure Function return new NewClosureFunction( func_node, context.buildClosureInitList(func_node), ); } export function buildExpression( expr: Expression, context: BuildContext, ): SemanticsValue { Logger.debug( `======= buildExpression: ${ts.SyntaxKind[expr.expressionKind]}`, ); let res: SemanticsValue | null = null; try { switch (expr.expressionKind) { case ts.SyntaxKind.PropertyAccessExpression: // EnumerateKeysExpression and PropertyAccessExpression has the same type kind if (expr instanceof EnumerateKeysExpression) { res = buildEnumerateKeysExpr(expr, context); } else { res = buildPropertyAccessExpression( expr as PropertyAccessExpression, context, ); } break; case ts.SyntaxKind.Identifier: res = buildIdentiferExpression( expr as IdentifierExpression, context, ); break; case ts.SyntaxKind.NullKeyword: res = new LiteralValue(Primitive.Null, null); break; case ts.SyntaxKind.UndefinedKeyword: res = new LiteralValue(Primitive.Undefined, undefined); break; case ts.SyntaxKind.NumericLiteral: { const n = (expr as NumberLiteralExpression).expressionValue; if (isInt(expr)) { res = new LiteralValue(Primitive.Int, toInt(n)); } else { res = new LiteralValue(Primitive.Number, n); } break; } case ts.SyntaxKind.StringLiteral: res = new LiteralValue( Primitive.RawString, (expr as StringLiteralExpression).expressionValue, ); break; case ts.SyntaxKind.TrueKeyword: res = new LiteralValue(Primitive.Boolean, true); break; case ts.SyntaxKind.FalseKeyword: res = new LiteralValue(Primitive.Boolean, false); break; case ts.SyntaxKind.ObjectLiteralExpression: res = buildObjectLiteralExpression( expr as ObjectLiteralExpression, context, ); break; case ts.SyntaxKind.ArrayLiteralExpression: res = buildArrayLiteralExpression( expr as ArrayLiteralExpression, context, ); break; case ts.SyntaxKind.BinaryExpression: res = buildBinaryExpression(expr as BinaryExpression, context); break; case ts.SyntaxKind.CommaToken: res = buildCommaExpression(expr as CommaExpression, context); break; case ts.SyntaxKind.ConditionalExpression: res = buildConditionalExpression( expr as ConditionalExpression, context, ); break; case ts.SyntaxKind.CallExpression: res = buildCallExpression(expr as CallExpression, context); break; case ts.SyntaxKind.SuperKeyword: res = buildSuperExpression(expr as SuperExpression, context); break; case ts.SyntaxKind.ThisKeyword: res = buildThisValue2(context); break; case ts.SyntaxKind.NewExpression: res = buildNewExpression2(expr as NewExpression, context); break; case ts.SyntaxKind.ElementAccessExpression: res = buildElementAccessExpression( expr as ElementAccessExpression, context, ); break; case ts.SyntaxKind.AsExpression: res = buildAsExpression(expr as AsExpression, context); break; case ts.SyntaxKind.FunctionExpression: case ts.SyntaxKind.ArrowFunction: res = buildFunctionExpression( (expr as FunctionExpression).funcScope, context, ); break; case ts.SyntaxKind.ParenthesizedExpression: res = buildExpression( (expr as ParenthesizedExpression).parentesizedExpr, context, ); break; case ts.SyntaxKind.PrefixUnaryExpression: case ts.SyntaxKind.PostfixUnaryExpression: res = buildUnaryExpression(expr as UnaryExpression, context); break; case ts.SyntaxKind.TypeOfExpression: res = buildTypeOfExpression(expr as TypeOfExpression, context); break; case ts.SyntaxKind.SpreadElement: { const targetValue = buildExpression( (expr as SpreadExpression).target, context, ); res = new SpreadValue(targetValue.type, targetValue); break; } case ts.SyntaxKind.TemplateExpression: { res = buildTemplateExpression( expr as TemplateExpression, context, ); break; } } if (res == null) { res = new UnimplementValue(expr.tsNode!); } if (getConfig().sourceMap && expr.tsNode) { res.location = getNodeLoc(expr.tsNode); } return res; } catch (e: any) { Logger.error(e); const tsNode = expr.tsNode; /** sometimes Expression maybe convert from other Expression for convenience * so the tsNode maybe undefined */ if (!tsNode) { throw Error(e); } const sourceFile = tsNode.getSourceFile(); const start = tsNode.getStart(sourceFile); const startLineInfo = sourceFile.getLineAndCharacterOfPosition(start); Logger.error( `[ERROR] @ "${sourceFile.fileName}" line: ${ startLineInfo.line + 1 } @${ startLineInfo.character } end: ${tsNode.getEnd()} width: ${tsNode.getWidth(sourceFile)}`, ); Logger.error(`Source: ${tsNode.getFullText(sourceFile)}`); throw Error(e); } } ================================================ FILE: src/semantics/flatten.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import ts from 'typescript'; import { ValueType, ValueTypeKind, PrimitiveType, Primitive, ArrayType, SetType, MapType, UnionType, TypeParameterType, FunctionType, EnumType, ObjectType, } from './value_types.js'; import { createType, isObjectType, isNullValueType, createUnionType, createArrayType, } from './type_creator.js'; import { SemanticsValueKind, SemanticsValue, NopValue, VarValue, VarValueKind, ThisValue2, SuperValue, LiteralValue, BinaryExprValue, PrefixUnaryExprValue, PostUnaryExprValue, ConditionExprValue, CastValue, NewClassValue, InstanceOfValue, FunctionCallBaseValue, ElementSetValueKind, ElementSetValue, ElementGetValueKind, ElementGetValue, FunctionCallValue, ConstructorCallValue, ToStringValue, ValueBinaryOperator, NewClosureFunction, UnimplementValue, DynamicSetValue, DynamicGetValue, DynamicCallValue, ShapeSetValue, ShapeGetValue, ShapeCallValue, VTableGetValue, VTableCallValue, VTableSetValue, OffsetCallValue, OffsetGetValue, OffsetSetValue, OffsetSetterValue, OffsetGetterValue, DirectCallValue, DirectSetterValue, DirectGetterValue, MemberGetValue, MemberSetValue, MemberCallValue, NewLiteralObjectValue, NewLiteralArrayValue, NewConstructorObjectValue, NewFromClassObjectValue, ReturnValue, BlockValue, BlockBranchValue, BlockBranchIfValue, } from './value.js'; import { SemanticsNode, SemanticsKind, FunctionDeclareNode, VarDeclareNode, ModuleNode, ForNode, IfNode, BasicBlockNode, BlockNode, SwitchNode, CaseClauseNode, DefaultClauseNode, WhileNode, ReturnNode, } from './semantics_nodes.js'; export function flattenConditionValue( cond: ConditionExprValue, ): SemanticsValue { const outter = new BlockValue(cond.falseExpr.type, 'condition_outter'); const falseBlock = new BlockValue(cond.trueExpr.type, 'condition_false'); const trueBlock = new BlockValue(cond.trueExpr.type, 'condition_true'); /* TODO: flatten here may be a mistake, since binaryen don't allow return in block $condition_false, which will add a drop manually.*/ /* condition ? trueExpr : FalseExpr block $outter block $condition_false branch_if condition false branch $condition_false block $condition_true trueExpr end branch $outter end falseExpr end end */ trueBlock.addValue(cond.trueExpr); falseBlock.addValue( new BlockBranchIfValue( new BlockBranchValue(falseBlock), cond.condition, false, ), ); falseBlock.addValue(trueBlock); falseBlock.addValue(new BlockBranchValue(outter)); outter.addValue(falseBlock); outter.addValue(cond.falseExpr); outter.shape = cond.shape; return outter; } export class FlattenContext { private _labelIndex = 0; private _stacks: BlockValue[] = []; push(block: BlockValue) { this._stacks.push(block); } pop() { this._stacks.pop(); } addValue(value: SemanticsValue) { if (this._stacks.length > 0) { this._stacks[this._stacks.length - 1].addValue(value); } } pushAdd(block: BlockValue) { this.addValue(block); this.push(block); } makeLabel(hint: string): string { this._labelIndex++; return `${hint}${this._labelIndex}`; } findNearestLoop(): BlockValue | undefined { if (this._stacks.length <= 0) return undefined; let block: BlockValue | undefined = this._stacks[this._stacks.length - 1]; while (block && block.isLoop) { block = block.parent; } return block; } findNearestBreakTarget(): BlockValue | undefined { if (this._stacks.length <= 0) return undefined; let block: BlockValue | undefined = this._stacks[this._stacks.length - 1]; while (block && block.breakTarget) block = block.parent; return block; } } function buildVarReferenceList( varList?: VarDeclareNode[], ): VarDeclareNode[] | undefined { if (varList) { const vl = varList.filter((v) => v.isUsedInClosureByRef()); if (vl.length > 0) return vl; } return undefined; } export function flatternStatement( node: SemanticsNode, context: FlattenContext, ) { switch (node.kind) { case SemanticsKind.BASIC_BLOCK: node.forEachValue((v) => context.addValue(v)); break; case SemanticsKind.RETURN: context.addValue(new ReturnValue((node as ReturnNode).expr)); break; case SemanticsKind.BREAK: { const breakable_block = context.findNearestLoop(); if (breakable_block) { context.addValue(new BlockBranchValue(breakable_block)); } else { // error throw Error(`break unkown block`); } break; } case SemanticsKind.CONTINUE: { const loop_block = context.findNearestLoop(); if (loop_block) { context.addValue(new BlockBranchValue(loop_block)); } break; } case SemanticsKind.BLOCK: { const blockNode = node as BlockNode; if (blockNode.varList) { const new_block = new BlockValue( Primitive.Void, context.makeLabel('block_node'), ); new_block.varList = blockNode.varList; new_block.refList = buildVarReferenceList(blockNode.varList); context.pushAdd(new_block); node.forEachChild((n) => flatternStatement(n, context)); context.pop(); } else { node.forEachChild((n) => flatternStatement(n, context)); } break; } case SemanticsKind.IF: { const ifNode = node as IfNode; const if_block = new BlockValue( Primitive.Void, context.makeLabel('if'), ); const true_block = new BlockValue( Primitive.Void, context.makeLabel('if_true'), ); context.pushAdd(if_block); if (ifNode.falseNode) { /* * if (condition) { trueNode } else { falseNode } * * block $if * block $if_false * branch_if condition false branch $if_false * trueNode * branch $if * end * falseNode * end * end */ const false_block = new BlockValue( Primitive.Void, context.makeLabel('if_false'), ); true_block.addValue( new BlockBranchIfValue( new BlockBranchValue(false_block), ifNode.condition, false, ), ); context.push(true_block); flatternStatement(ifNode.trueNode, context); context.pop(); true_block.addValue(new BlockBranchValue(if_block)); false_block.addValue(true_block); context.push(false_block); flatternStatement(ifNode.falseNode, context); context.pop(); if_block.addValue(false_block); } else { /* * if (condition) { trueNode } * * block $if * branch_if condition false branch $if * trueNode * end * end */ true_block.addValue( new BlockBranchIfValue( new BlockBranchValue(if_block), ifNode.condition, false, ), ); context.push(true_block); flatternStatement(ifNode.trueNode, context); context.pop(); if_block.addValue(true_block); } context.pop(); break; } case SemanticsKind.WHILE: /* falls through */ case SemanticsKind.DOWHILE: { /* while(condition) { body } block $while loop $while_loop branch_if condition false $while body branch $while_loop // loop end // while loop end do { body } while(condition) block $do_while loop $do_while_loop body branch_if condition true $do_while_loop branch $do_while end end */ const whileNode = node as WhileNode; const is_dowhile = node.kind == SemanticsKind.DOWHILE; const hint_prefix = is_dowhile ? 'do_while' : 'while'; const while_block = new BlockValue( Primitive.Void, hint_prefix, false, true, ); const loop_block = new BlockValue( Primitive.Void, context.makeLabel(`${hint_prefix}_loop`), true, ); context.pushAdd(while_block); context.pushAdd(loop_block); if (is_dowhile) { if (whileNode.body) { flatternStatement(whileNode.body, context); } loop_block.addValue( new BlockBranchIfValue( new BlockBranchValue(loop_block), whileNode.condition, true, ), ); loop_block.addValue(new BlockBranchValue(while_block)); } else { loop_block.addValue( new BlockBranchIfValue( new BlockBranchValue(while_block), whileNode.condition, false, ), ); if (whileNode.body) flatternStatement(whileNode.body, context); loop_block.addValue(new BlockBranchValue(loop_block)); } context.pop(); context.pop(); break; } case SemanticsKind.FOR: { /* for (initialize; condition; next) { body } block $for initialize loop $for_loop branch_if condition false $for block $for_body body // for continue to break to loop end next branch $for_loop end end */ const forNode = node as ForNode; const for_block = new BlockValue( Primitive.Void, context.makeLabel('for'), false, true, ); context.pushAdd(for_block); const for_loop = new BlockValue( Primitive.Void, context.makeLabel('for_loop'), true, ); if (forNode.varList) { for_block.varList = forNode.varList; for_block.refList = buildVarReferenceList(forNode.varList); } if (forNode.initialize) flatternStatement(forNode.initialize, context); context.pushAdd(for_loop); if (forNode.condition) { for_loop.addValue( new BlockBranchIfValue( new BlockBranchValue(for_block), forNode.condition, false, ), ); } if (forNode.body) { const body_block = new BlockValue( Primitive.Void, context.makeLabel('for_body'), ); context.pushAdd(body_block); flatternStatement(forNode.body, context); context.pop(); } if (forNode.next) for_loop.addValue(forNode.next); // add loop for_loop.addValue(new BlockBranchValue(for_loop)); context.pop(); context.pop(); break; } case SemanticsKind.SWITCH: { const switchNode = node as SwitchNode; /* switch(condition) { caseClause .. defaultClause } block $switch block $switch_default block $switch_caseN block $switch_caseN-1 block $switch_caseN-2 ... block $switch_case1 branch_if case1_condition false $blcok $switch_case2 case1_body branch $switch end branch_if case2_condition false $block $switch_case3 case2_body breach $switch ... end end end end end */ const switch_block = new BlockValue( Primitive.Void, context.makeLabel('switch'), false, true, ); context.pushAdd(switch_block); if (switchNode.defaultClause) { const def_block = new BlockValue( Primitive.Void, context.makeLabel('switch_default'), ); context.pushAdd(def_block); if (switchNode.defaultClause.body) flatternStatement(switchNode.defaultClause.body, context); } for (let i = switchNode.caseClause.length - 1; i >= 0; i--) { const case_node = switchNode.caseClause[i]; const case_block = new BlockValue( Primitive.Void, context.makeLabel(`switch_case${i}_`), ); context.pushAdd(case_block); context.addValue( new BlockBranchIfValue( new BlockBranchValue(case_block.parent!), case_node.caseVar, false, ), ); if (case_node.body) flatternStatement(case_node.body, context); context.pop(); } if (switchNode.defaultClause) { context.pop(); } context.pop(); break; } case SemanticsKind.FOR_IN: /* falls through */ case SemanticsKind.FOR_OF: /* for (const key in/of target) body block $for varList (key) get_key_iter/get_value_iter loop $for_loop branch_if it_is_null false $for key = it.next body branch $for_loop end end */ break; } } export function flattenFunction(func: FunctionDeclareNode) { if (func.body) { const context = new FlattenContext(); const block = new BlockValue(Primitive.Void, ''); context.push(block); block.varList = func.varList; const refList = buildVarReferenceList(block.varList); const paramRefList = buildVarReferenceList(func.parameters); if (refList && paramRefList) block.refList = paramRefList.concat(refList); else if (refList) block.refList = refList; else if (paramRefList) block.refList = paramRefList; flatternStatement(func.body, context); context.pop(); func.flattenValue = block; } } ================================================ FILE: src/semantics/index.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { SemanticsNode, ModuleNode, VarDeclareNode, VarStorageType, BasicBlockNode, BlockNode, FunctionDeclareNode, FunctionOwnKind, ParameterNodeFlag, ExternModule, ExternType, ExternTypeKind, NativeSignature, } from './semantics_nodes.js'; import { Logger } from '../log.js'; import { ParserContext } from '../frontend.js'; import { TSClass, TSInterface, TypeResolver } from '../type.js'; import { Parameter } from '../variable.js'; import { ValueType, ValueTypeKind, Primitive, FunctionType, ObjectType, EnumType, } from './value_types.js'; import { PredefinedTypeId, isNativeSignatureComment, processGenericType, } from '../utils.js'; import { GetPredefinedType } from './predefined_types.js'; import { flattenFunction } from './flatten.js'; import { BuildContext, SymbolKey, SymbolValue } from './builder_context.js'; import { SemanticsValueKind, SemanticsValue, VarValue } from './value.js'; import { Scope, ScopeKind, GlobalScope, FunctionScope, ClassScope, BlockScope, NamespaceScope, ClosureEnvironment, } from '../scope.js'; import { buildExpression, shapeAssignCheck } from './expression_builder.js'; import { createType, createObjectDescriptionShapes } from './type_creator.js'; import { createFromVariable, buildStatement, createLocalSymbols, } from './statement_builder.js'; import { CreateDefaultDumpWriter } from './dump.js'; import { Variable } from '../variable.js'; import { ProcessBuiltinObjectSpecializeList, ForEachBuiltinObject, builtin_objects, } from './builtin.js'; import { ModDeclStatement, Statement } from '../statement.js'; import { IdentifierExpression } from '../expression.js'; import { BuiltinNames } from '../../lib/builtin/builtin_name.js'; import { getConfig } from '../../config/config_mgr.js'; function processTypes(context: BuildContext, globalScopes: Array) { for (const scope of globalScopes) { scope.traverseScopTree((scope) => { context.push(scope); scope.namedTypeMap.forEach((type, name) => { Logger.debug(`== type: ${name}, ${type.kind}`); createType(context, type); }); context.pop(); }); } } function buildStatements( context: BuildContext, scopeStmts: Statement[], ): SemanticsNode[] { const statements: SemanticsNode[] = []; let basic_block: BasicBlockNode | undefined = undefined; for (const statement of scopeStmts) { if (statement instanceof ModDeclStatement) { const nsStmts = generateNamespaceScopeNodes( context, statement.scope, ); statements.push(...nsStmts); } else { const r = buildStatement(statement, context); if (r instanceof SemanticsValue) { if (!basic_block) { basic_block = new BasicBlockNode(); statements.push(basic_block); } basic_block!.pushSemanticsValue(r as SemanticsValue); } else { basic_block = undefined; statements.push(r as SemanticsNode); } } } return statements; } function processGlobalStatements(context: BuildContext, g: GlobalScope) { /* calculate all vars contained in this scope */ const allVars: Variable[] = []; getAllVars(g, allVars, g); /* get all global statements, including globalScope's statements and namespaceScope's statements */ /* Hint: the statements' order can't be guaranteed */ const globalStartStmts = buildStatements(context, g.statements); const curStartStmts = context.startStmts.get(g)!.concat(globalStartStmts); context.startStmts.set(g, curStartStmts); curStartStmts.forEach((s) => { if (s instanceof BasicBlockNode) { s.isStartBasicBlock = true; } }); const globalStart = new FunctionDeclareNode( g.startFuncName, FunctionOwnKind.START, GetPredefinedType(PredefinedTypeId.FUNC_VOID_VOID_NONE) as FunctionType, new BlockNode(curStartStmts), ); globalStart.debugFilePath = g.debugFilePath; if (g === context.enterScope) { globalStart.isInEnterScope = true; } if (g.importStartFuncNameList.length > 0) { globalStart.importStartFuncNameList = g.importStartFuncNameList; } flattenFunction(globalStart); context.module.functions.add(globalStart); /* add global init function declaration */ const gloablInitFunc = new FunctionDeclareNode( BuiltinNames.globalInitFuncName, FunctionOwnKind.DEFAULT, GetPredefinedType(PredefinedTypeId.FUNC_VOID_VOID_NONE) as FunctionType, new BlockNode([]), ); context.module.globalInitFunc = gloablInitFunc; } export function getFunctionOwnKind(f: FunctionScope): number { let funcKind = 0; if (f.isDeclare()) { funcKind |= FunctionOwnKind.DECLARE; } if (f.isMethod()) { funcKind |= FunctionOwnKind.METHOD; } else { funcKind |= FunctionOwnKind.DEFAULT; } if (f.isStatic()) { funcKind |= FunctionOwnKind.STATIC; } if (f.isDecorator()) { funcKind |= FunctionOwnKind.DECORATOR; } if (f.isExport()) { funcKind |= FunctionOwnKind.EXPORT; } return funcKind; } function getMethodClassType( f: FunctionScope, context: BuildContext, ): ObjectType | undefined { if (!f.parent || f.parent.kind != ScopeKind.ClassScope) return undefined; const symbol = context.globalSymbols.get(f.parent!) as ValueType; // get the class type if (!symbol || !(symbol instanceof SemanticsValue)) { return undefined; } const type = (symbol! as unknown as SemanticsValue).type; if (!(type instanceof ObjectType)) { throw Error( `${type} must be object of function scope ${f.mangledName}`, ); } return type as ObjectType; } function createFunctionDeclareNode( context: BuildContext, f: FunctionScope, ): FunctionDeclareNode { let name = f.mangledName; //f.funcName; // iff the function is a static member function. /** * adding a '@' prefix before the static member function name * is to distinguish the non-static member function with the same name. */ if (f.className != '' && f.funcType.isStatic) { const reverse = name.split('|').reverse(); reverse[0] = '@' + reverse[0]; name = reverse.reverse().join('|'); } /* maybe can be replace to context.findSymbolKey(f.funcType) as FunctionType */ const func_type = createType(context, f.funcType) as FunctionType; const this_type = getMethodClassType(f, context); const parameters: VarDeclareNode[] = []; const paramArray = f.paramArray; for (let i = 0; i < paramArray.length; i++) { const p = paramArray[i]; let p_type: ValueType | undefined = undefined; p_type = func_type.argumentsType[i]; const initValue = p.initExpression ? buildExpression(p.initExpression, context) : undefined; Logger.debug( `=== paramter[${i}] ${name} ${p.varName} ${p_type} argument index: ${i}`, ); const param = new VarDeclareNode( SemanticsValueKind.PARAM_VAR, p_type ?? Primitive.Any, p.varName, p.varIndex, p.isOptional ? ParameterNodeFlag.OPTIONAL : 0, initValue, p.closureIndex, p.belongCtx ? createFromVariable(p.belongCtx, false, context) : undefined, p.initContext ? createFromVariable(p.initContext, false, context) : undefined, ); parameters.push(param); } const parentClosureEnvScope = f.parent?.getNearestClosureEnvironment(); const parentCtx = parentClosureEnvScope ? createFromVariable(parentClosureEnvScope.varArray[0], false, context) : undefined; const func = new FunctionDeclareNode( name, getFunctionOwnKind(f), func_type, new BlockNode([]), parameters, undefined, parentCtx, this_type, ); func.debugFilePath = f.debugFilePath; for (const comment of f.comments) { if (isNativeSignatureComment(comment)) { const paramValueTypes: ValueType[] = []; for (const paramType of comment.paramTypes) { paramValueTypes.push(createType(context, paramType)); } const returnValueType = createType(context, comment.returnType); const obj: NativeSignature = { paramTypes: paramValueTypes, returnType: returnValueType, }; func.comments.push(obj); } else { func.comments.push(comment); } } return func; } function processScopesGlobalObjs(context: BuildContext, scopes: Scope[]) { for (const scope of scopes) { processGlobalObjs(context, scope); } } function isInClosureScope(scope: Scope): boolean { while (scope.parent) { scope = scope.parent!; if (scope instanceof ClosureEnvironment) { return true; } } return false; } function processGlobalObjs(context: BuildContext, scope: Scope) { context.push(scope); if (scope.kind == ScopeKind.FunctionScope) { const func = createFunctionDeclareNode(context, scope as FunctionScope); if (scope.parent === context.enterScope) { func.isInEnterScope = true; } Logger.debug( `==== processGlobalObjs Function ${scope.getName()} func: ${ func.name }`, ); const var_func = new VarValue( SemanticsValueKind.GLOBAL_CONST, func.funcType, func, func.name, ); context.globalSymbols.set(scope, var_func); context.addFunctionValue(var_func); context.module.functions.add(func); } else if (scope.kind == ScopeKind.ClassScope) { const class_scope = scope as ClassScope; const type = createType(context, class_scope.classType); // create a var value const obj_type = type as ObjectType; const class_value = new VarValue( SemanticsValueKind.GLOBAL_CONST, obj_type.classType!, obj_type.classType!, obj_type.meta.name, ); class_value.shape = obj_type.classType!.meta.originShape; // TODO context.globalSymbols.set(class_scope, class_value); context.globalSymbols.set(class_scope.classType, class_value); context.addClassValue(class_value); } else if ( scope.kind == ScopeKind.GlobalScope || scope.kind == ScopeKind.NamespaceScope ) { // put the scope into symbols table, so that the scope can be import const name = scope.mangledName; const ns_var = new VarValue( SemanticsValueKind.GLOBAL_CONST, Primitive.Namespace, scope, name, ); context.globalSymbols.set(scope, ns_var); for (const v of scope.varArray) { if (v.isLocalVar()) continue; Logger.debug( `=== processGlobalVars ${v.mangledName} ${ v.varName } declare ${v.isDeclare()}`, ); const storage: VarStorageType = v.isConst() || v.isReadOnly() ? SemanticsValueKind.GLOBAL_CONST : SemanticsValueKind.GLOBAL_VAR; let type = context.module.findValueTypeByType(v.varType); //let type = context.findSymbolKey(v.varType) as ValueType; if (!type) type = Primitive.Any; const var_decl = new VarDeclareNode( storage, type, v.mangledName, context.module.globalVars.length, 0, // TODO: replace with flag: like declare ); const var_value = new VarValue( storage, type, var_decl, v.mangledName, ); context.module.globalVars.push(var_decl); // TODO removed context.globalSymbols.set(v, var_value); context.addGlobalValue(v.mangledName, var_value); } } processScopesGlobalObjs(context, scope.children); context.pushTask(() => context.pop()); } function InitGlobalObj(context: BuildContext, g: GlobalScope) { for (const v of g.varArray) { if (v.isLocalVar()) continue; Logger.debug( `=== InitGlobalObj ${v.mangledName} ${ v.varName } declare ${v.isDeclare()}`, ); const varValue = context.globalSymbols.get(v)! as VarValue; let init_value: SemanticsValue | undefined; if (v.initExpression != null) { init_value = buildExpression(v.initExpression, context); if (varValue.ref instanceof VarDeclareNode) { varValue.ref.initValue = init_value; if ( shapeAssignCheck(varValue.effectType, init_value.effectType) ) { varValue.shape = init_value.shape; } } } } } function foreachScopeChildren(context: BuildContext, scope: Scope) { for (const c of scope.children) { generateScopeNodes(context, c); } } export function generateChildrenFunctionScope( context: BuildContext, scope: Scope, ) { for (const c of scope.children) { if (c.kind == ScopeKind.FunctionScope) { generateFunctionScopeNodes(context, c as FunctionScope); } else if (c.kind == ScopeKind.ClassScope) { generateClassScopeNodes(context, c as ClassScope); } } } function getAllVars( scope: Scope, vars: Variable[], belongScope: FunctionScope | GlobalScope, ) { if (scope instanceof FunctionScope && scope !== belongScope) { return; } for (const variable of scope.varArray) { vars.push(variable); } for (const child of scope.children) { getAllVars(child, vars, belongScope); } } function generateFunctionScopeNodes( context: BuildContext, scope: FunctionScope, ) { const symbol = context.globalSymbols.get(scope); let func: FunctionDeclareNode | undefined = undefined; if (symbol) { if (symbol instanceof VarValue) { const var_value = symbol as VarValue; if (var_value.type.kind == ValueTypeKind.FUNCTION) { func = var_value.ref as FunctionDeclareNode; } } else if (symbol instanceof FunctionDeclareNode) { func = symbol as FunctionDeclareNode; } } if (!func) { func = createFunctionDeclareNode(context, scope); context.module.functions.add(func); } if ((func.ownKind & FunctionOwnKind.DECLARE) !== 0) return; Logger.debug(`=======>> begin build function ${func}====`); /* build parameter symbols */ const params = new Map(); if (func.parameters && func.parameters.length > 0) { for (let i = 0; i < scope.paramArray.length; i++) { // must use scope.paramArray const p = scope.paramArray[i]; const n = func.parameters[i]; Logger.debug(`=== params :${p.varName} ${n.toString()}, ${n.type}`); params.set(p, new VarValue(n.storageType, n.type, n, n.index)); } } /* calculate all vars contained in this scope */ const allVars: Variable[] = []; getAllVars(scope, allVars, scope); context.push(scope, params, func); const [local_varlist, local_symbols] = createLocalSymbols(scope, context); func.varList = local_varlist; if (local_symbols) { context.updateNamedSymbolByScope(scope, local_symbols); } generateChildrenFunctionScope(context, scope); const statements = buildStatements(context, scope.statements); func.body.statements = statements; context.pop(); flattenFunction(func); Logger.debug(`=======<< end build function ${func}====`); } function generateClassScopeNodes(context: BuildContext, scope: ClassScope) { foreachScopeChildren(context, scope); } function generateBlockScopeNodes(context: BuildContext, scope: BlockScope) { return; } function generateNamespaceScopeNodes( context: BuildContext, scope: NamespaceScope, ) { context.push(scope); /* Handle statements in namespaceScope */ const nsStartStmts = buildStatements(context, scope.statements); context.pop(); return nsStartStmts; } function generateScopeNodes(context: BuildContext, scope: Scope) { switch (scope.kind) { case ScopeKind.FunctionScope: generateFunctionScopeNodes(context, scope as FunctionScope); break; case ScopeKind.ClassScope: generateClassScopeNodes(context, scope as ClassScope); break; case ScopeKind.BlockScope: generateBlockScopeNodes(context, scope as BlockScope); break; default: foreachScopeChildren(context, scope); break; } } function processGlobals(context: BuildContext, parserContext: ParserContext) { context.enterScope = parserContext.globalScopes[parserContext.globalScopes.length - 1]; processTypes(context, parserContext.globalScopes); processScopesGlobalObjs(context, parserContext.globalScopes); /* must call brfore processObjectDescriptions, so we can ensure that the meta information has been completed before creating the shape */ context.runAllTasks(); processObjectDescriptions(context); processImportsExports(context, parserContext); for (const g of parserContext.globalScopes) { context.push(g); context.startStmts.set(g, []); // global variables may be processed in the 'generateScopeNodes' function. // So we need to initialize global variables before using them. InitGlobalObj(context, g); generateScopeNodes(context, g); processGlobalStatements(context, g); context.pop(); } } function addImportNamespaceItems( context: BuildContext, ns: NamespaceScope, m: ExternModule, name: string, ) { const ns_delcared = ns.isDeclare(); for (const v of ns.varArray) { if (ns_delcared || v.isDeclare()) { const found = context.findSymbolKey(v); if (found) addExternItem(context, m, `${name}|${v.varName}`, found, true); } } // add the scopes for (const scope of ns.children) { if (!(ns_delcared || scope.isDeclare())) continue; switch (scope.kind) { case ScopeKind.FunctionScope: { const found = context.findSymbolKey(scope); if (found) addExternItem( context, m, `${name}|${(scope as FunctionScope).funcName}`, found, true, ); break; } case ScopeKind.ClassScope: { const found = context.findSymbolKey(scope); if (found) addExternItem( context, m, `${name}|${(scope as ClassScope).className}`, found, true, ); break; } case ScopeKind.NamespaceScope: { addImportNamespaceItems( context, scope as NamespaceScope, m, `${name}|${scope.getName()}`, ); break; } } } } function addExportNamespaceItems( context: BuildContext, ns: NamespaceScope, m: ExternModule, name: string, ) { const ns_export = ns.isExport(); // add the variable for (const v of ns.varArray) { if (ns_export || v.isExport()) { const found = context.findSymbolKey(v); if (found) addExternItem(context, m, `${name}|${v.varName}`, found, false); } } // add the scopes for (const scope of ns.children) { if (!(ns_export || scope.isExport())) continue; switch (scope.kind) { case ScopeKind.FunctionScope: { const found = context.findSymbolKey(scope); if (found) addExternItem( context, m, `${name}|${(scope as FunctionScope).funcName}`, found, false, ); break; } case ScopeKind.ClassScope: { const found = context.findSymbolKey(scope); if (found) addExternItem( context, m, `${name}|${(scope as ClassScope).className}`, found, false, ); break; } case ScopeKind.NamespaceScope: { addExportNamespaceItems( context, scope as NamespaceScope, m, `${name}|${scope.getName()}`, ); break; } } } } function addExternItem( context: BuildContext, m: ExternModule, name: string, value: SymbolValue, isImport: boolean, ) { let kind: ExternTypeKind; let rel_val: any = value; if (value instanceof VarValue) { const var_val = value as VarValue; if ( var_val.kind == SemanticsValueKind.GLOBAL_VAR || var_val.kind == SemanticsValueKind.GLOBAL_CONST ) { rel_val = var_val.ref; } else { throw Error( `Only Global value can be import export: ${value} in module ${m.name}`, ); } } if (rel_val instanceof VarDeclareNode) { kind = ExternTypeKind.VAR; } else if (rel_val instanceof FunctionDeclareNode) { kind = ExternTypeKind.FUNCTION; } else if (rel_val instanceof EnumType) { kind = ExternTypeKind.ENUM; } else if (rel_val instanceof ObjectType) { const obj_type = rel_val as ObjectType; if (obj_type.kind == ValueTypeKind.ARRAY) return; // ignore the builtin export/import else if (obj_type.classType) kind = ExternTypeKind.CLASS; else if (obj_type.isLiteralObject()) kind = ExternTypeKind.VAR; else return; // ignore the interface } else if (rel_val instanceof NamespaceScope) { // NAMESPACE if (isImport) addImportNamespaceItems( context, rel_val as NamespaceScope, m, name, ); else addExportNamespaceItems( context, rel_val as NamespaceScope, m, name, ); return; } else { Logger.info(`Type ${value} export or import in module ${m.name}`); return; } Logger.debug( `=== Add${m.isImport ? 'Import' : 'Export'} ${m.name} ${name} ${ ExternTypeKind[kind] } ${rel_val} `, ); m.addItem(kind, name, rel_val as ExternType); } function processImportsExports( context: BuildContext, parserContext: ParserContext, ) { const module = context.module; const exports = module.exports; const imports = module.imports; for (const g of parserContext.globalScopes) { const exportList = g.exportIdentifierList; const importList = g.declareIdentifierList; context.push(g); if (exportList.length > 0) { const export_module = new ExternModule(g.moduleName, false); for (const id of exportList) { let ret_val: SymbolValue | undefined; let export_name: string; // TODO process export xxx from ''; if (id instanceof IdentifierExpression) { ret_val = context.findSymbol(id.identifierName); export_name = id.identifierName; if (!ret_val) { throw Error( `Cannot find the export "${id}" in "${g.moduleName}"`, ); } if ( ret_val instanceof VarValue && ret_val.ref instanceof FunctionDeclareNode && g == context.enterScope ) { ret_val.ref.isInEnterScope = true; } } else { ret_val = buildExpression(id, context); export_name = id.expressionKind.toString(); } addExternItem( context, export_module, export_name, ret_val!, false, ); } exports.add(export_module); } if (importList.size > 0) { const import_module = new ExternModule(g.moduleName, true); for (const id of importList) { const ret_val = context.findSymbol(id); if (!ret_val) { throw Error( `Cannot find the import "${id}" in "${g.moduleName}"`, ); } addExternItem(context, import_module, id, ret_val!, true); } imports.add(import_module); } context.pop(); } } function processObjectDescriptions(context: BuildContext) { // generate the originShape context.objectDescriptions.forEach((meta, name) => { createObjectDescriptionShapes(context, meta); }); ForEachBuiltinObject((obj_type: ObjectType) => { createObjectDescriptionShapes(context, obj_type.instanceType!.meta); createObjectDescriptionShapes(context, obj_type.classType!.meta); }); ProcessBuiltinObjectSpecializeList(); } function setRecArraySize(recClass: TSClass[][], recObjectType: ObjectType[][]) { if (recClass.length == 0) { return recObjectType; } recObjectType = new Array(recClass.length); for (let i = 0; i < recClass.length; ++i) { recObjectType[i] = new Array(recClass[i].length); } return recObjectType; } /** to avoid interface in rec circles */ function removeRecWhichHasInfc(recGroupTypes: TSClass[][]) { const recGroupTypesNew: TSClass[][] = []; if (recGroupTypes.length == 0) { return recGroupTypesNew; } for (let i = 0; i < recGroupTypes.length; ++i) { let hasInfc = false; for (let j = 0; j < recGroupTypes[i].length; ++j) { if (recGroupTypes[i][j] instanceof TSInterface) { hasInfc = true; break; } } if (!hasInfc) { recGroupTypesNew.push(recGroupTypes[i]); } } return recGroupTypesNew; } export function BuildModuleNode(parserContext: ParserContext): ModuleNode { const module = new ModuleNode(); const context = new BuildContext(parserContext.typeId, module); context.recClassTypeGroup = removeRecWhichHasInfc( parserContext.recGroupTypes, ); parserContext.recGroupTypes = []; context.module.recObjectTypeGroup = setRecArraySize( context.recClassTypeGroup, context.module.recObjectTypeGroup, ); processGlobals(context, parserContext); context.finishBuild(); if (getConfig().dumpSemanticTree) { module.dump(CreateDefaultDumpWriter()); module.dumpCodeTrees(CreateDefaultDumpWriter()); } context.recClassTypeGroup = []; return module; } ================================================ FILE: src/semantics/internal.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ function getClassMetaName(name: string): string { return `@${name}`; } function getClassPrototypeName(name: string): string { return `@_proto_${name}`; } export const InternalNames = { CONSTRUCTOR: 'constructor', CALLBACK: '@callback', NEW: '@new', getClassMetaName, getClassPrototypeName, }; ================================================ FILE: src/semantics/ir/data_pool.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ const INC_SIZE = 1024 * 4; export class DataPool { private buffer: Uint8Array; private capacity: number; private size: number; private strMap = new Map(); constructor() { this.buffer = new Uint8Array(INC_SIZE); this.capacity = INC_SIZE; this.size = 0; } addString(s: string): number { if (this.strMap.has(s)) return this.strMap.get(s)!; this.updateBufferSize(s.length); const size = this.size; let i; for (i = 0; i < s.length; i++) { const byte = s.charCodeAt(i); if (byte >= 256) throw Error('UTF-16 string not supported'); this.buffer[size + i] = byte; } i = s.length + size; this.buffer[i++] = 0; while (i % 4 != 0) { this.buffer[i++] = 0; } this.size = i; return size; } getStringCount(): number { return this.strMap.size; } addByte(byte: number): number { this.updateBufferSize(1); this.buffer[this.size++] = byte & 0xff; return this.size - 1; } getCurrentSize(): number { return this.size; } addInt32(n: number): number { //littel endian const size = this.addByte(n & 0xff); this.addByte((n >> 8) & 0xff); this.addByte((n >> 16) & 0xff); this.addByte((n >> 24) & 0xff); return size; } setByte(idx: number, byte: number) { this.buffer[idx] = byte & 0xff; } setInt32(idx: number, n: number) { this.setByte(idx, n & 0xff); this.setByte(idx + 1, (n >> 8) & 0xff); this.setByte(idx + 2, (n >> 16) & 0xff); this.setByte(idx + 3, (n >> 24) & 0xff); } updateBufferSize(inc_size: number) { if (this.size + inc_size > this.capacity) { const capacity = this.capacity + INC_SIZE; const buffer = new Uint8Array(capacity); for (let i = 0; i < this.size; i++) { buffer[i] = this.buffer[i]; } this.capacity = capacity; this.buffer = buffer; } } getData(): Uint8Array { return new Uint8Array(this.buffer.buffer, 0, this.size); } } ================================================ FILE: src/semantics/ir/function.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import ts from 'typescript'; import { IRCode, IRBlock } from './ircode.js'; import { IRModule } from './irmodule.js'; import { GlobalContext, S } from './ir_context.js'; import { SemanticsKind, SemanticsNode, FunctionDeclareNode, FunctionOwnKind, VarDeclareNode, BlockNode, BasicBlockNode, IfNode, ForNode, ForInNode, ForOfNode, WhileNode, ReturnNode, } from '../semantics_nodes.js'; import { ObjectType, Primitive } from '../value_types.js'; import { SemanticsValue, SemanticsValueKind, VarValue, LiteralValue, BinaryExprValue, FunctionCallValue, ConditionExprValue, ElementGetValue, ElementSetValue, CastValue, NewLiteralObjectValue, NewConstructorObjectValue, NewFromClassObjectValue, ShapeGetValue, ShapeSetValue, ShapeCallValue, DynamicGetValue, DynamicSetValue, DynamicCallValue, VTableGetValue, VTableSetValue, VTableCallValue, DirectGetterValue, DirectSetterValue, DirectCallValue, OffsetGetValue, OffsetSetValue, OffsetGetterValue, OffsetSetterValue, OffsetCallValue, BlockValue, BlockBranchValue, BlockBranchIfValue, FunctionCallBaseValue, NewClosureFunction, NewArrayLenValue, NewArrayValue, NewLiteralArrayValue, ReturnValue, } from '../value.js'; import { ObjectDescription, Shape } from '../runtime.js'; import { isEqualOperator } from '../expression_builder.js'; class LocalVars { constructor( public readonly start: number, public readonly vars: VarDeclareNode[], ) {} findVar(v: VarDeclareNode): number { for (let i = 0; i < this.vars.length; i++) { const local_var = this.vars[i]; if (local_var === v || local_var.name == v.name) return i + this.start; } return -1; } } class Context { public maxTempCount = 0; public curTempCount = 0; private _blockMap = new Map(); private _blockStack: IRBlock[] = []; private _localVarsStack: LocalVars[] = []; private _varCount = 0; constructor( private _globalContext: GlobalContext, public _func: FunctionDeclareNode, _irFunc: IRFunction, ) { this.pushBlock(_irFunc); this._varCount = (this._func.varList ? this._func.varList.length : 0) + (this._func.flattenValue ? this.calcLocalVarCount(this._func.flattenValue!) : 0); this.pushLocalVars(this._func.varList); } pushLocalVars(varList?: VarDeclareNode[]) { if (!varList) return; let start = 0; if (this._localVarsStack.length > 0) { const s = this._localVarsStack[this._localVarsStack.length - 1]; start = s.start + s.vars.length; } this._localVarsStack.push(new LocalVars(start, varList!)); } get varCount(): number { return this._varCount; } getLocalVarIndex(value: VarDeclareNode): number { for (let i = this._localVarsStack.length - 1; i >= 0; i--) { const idx = this._localVarsStack[i].findVar(value); if (idx >= 0) return idx; } return -1; } getGlobalVarIndex(value: VarDeclareNode, s: S = S.LOAD): number { return this._globalContext.getVarIndex(value, s); } getFunctionIndex(func: FunctionDeclareNode, s: S = S.LOAD): number { return this._globalContext.getFunctionIndex(func, s); } getClassIndex(clazz: ObjectType, s: S = S.NEW): number { return this._globalContext.getClassIndex(clazz, s); } getMetaIndex(meta: ObjectDescription): number { return this._globalContext.getMetaIndex(meta); } getShapeIndex(shape: Shape): number { return this._globalContext.getShapeIndex(shape); } getClosureIndex(var_decl: VarDeclareNode): number { return this._func.findClosureIndex(var_decl); } getParameterIndex(var_decl: VarDeclareNode): number { return this._func.findParameterIndex(var_decl); } addString(s: string): number { return this._globalContext.module.dataPool.addString(s); } private calcLocalVarCount(expr: SemanticsValue): number { let count = 0; if (expr.kind != SemanticsValueKind.BLOCK) { return 0; } const block = expr as BlockValue; if (block.varList) count += block.varList.length; let child_count = 0; for (const v of block.values) { const c = this.calcLocalVarCount(v); if (c > child_count) child_count = c; } return count + child_count; } reset() { this.curTempCount = 0; } incTemp(n = 1) { this.curTempCount += n; this.update(); } decTemp(n = 1) { this.curTempCount -= n; } update() { if (this.maxTempCount < this.curTempCount) this.maxTempCount = this.curTempCount; } setBlock(block: BlockValue, ir_block: IRBlock) { this._blockMap.set(block, ir_block); } getIRBlock(block: BlockValue): IRBlock | undefined { return this._blockMap.get(block); } topBlock(): IRBlock { return this._blockStack[this._blockStack.length - 1]; } pushCode(op: IRCode) { this.topBlock().add(op); } pushBlock(ir_block: IRBlock) { this._blockStack.push(ir_block); } pushBlockVars(block: BlockValue) { this.pushLocalVars(block.varList); } popBlock() { this._blockStack.pop(); } popBlockVars(block: BlockValue) { if (block.varList) { this._localVarsStack.pop(); } } } export class IRFunction extends IRBlock { public paramCount = 0; public varCount = 0; public tempCount = 0; public closureCount = 0; public funcNode: FunctionDeclareNode | undefined; constructor(public vm: IRModule) { super('function', false); } get isStartFunction(): boolean { return ( !!this.funcNode && this.funcNode.ownKind == FunctionOwnKind.START ); } build(func: FunctionDeclareNode, global: GlobalContext) { this.funcNode = func; this.paramCount = func.parameters ? func.parameters.length : 0; if (func.flattenValue) { const context = new Context(global, func, this); this.buildValue(func.flattenValue!, context); this.tempCount = context.maxTempCount; this.varCount = context.varCount; this.closureCount = func.closureVars ? func.closureVars.length : 0; } else { // try to find at module infos const info = global.getImportInfo(func); if (info) { this.add( IRCode.NewImportFunction( info.module.externIndex, info.item.externIndex, ), ); } } } get name(): string { return this.funcNode!.name; } private buildVar( var_decl: VarDeclareNode, context: Context, load: boolean, ) { let idx = -1; let code: IRCode | undefined = undefined; if ( var_decl.storageType == SemanticsValueKind.LOCAL_VAR || var_decl.storageType == SemanticsValueKind.LOCAL_CONST ) { idx = context.getLocalVarIndex(var_decl); if (idx >= 0) { if (load) code = IRCode.LoadLocal(idx, var_decl.type); else code = IRCode.SaveLocal(idx, var_decl.type); } } else if (var_decl.storageType == SemanticsValueKind.PARAM_VAR) { idx = context.getParameterIndex(var_decl); if (idx >= 0) { if (load) code = IRCode.LoadParam(idx, var_decl.type); else code = IRCode.SaveParam(idx, var_decl.type); } } else { return; // ignroe } if (!code) { idx = context.getClosureIndex(var_decl); if (idx >= 0) { if (load) code = IRCode.LoadClosure(idx, var_decl.type); else code = IRCode.SaveClosure(idx, var_decl.type); } } if (!code) { throw Error( `buildVar Field, cannot found ${var_decl} in function: ${this.funcNode}`, ); } context.pushCode(code!); } private buildBlockRefList(context: Context, refList?: VarDeclareNode[]) { if (!refList) return; for (const v of refList!) { context.pushCode(IRCode.NewRef(v.type)); this.buildVar(v, context, false); } } private buildValue(value: SemanticsValue, context: Context) { switch (value.kind) { case SemanticsValueKind.BLOCK: { const block_value = value as BlockValue; const op = IRCode.NewBlock(block_value); context.pushCode(op); context.setBlock(block_value, op.block); context.pushBlockVars(block_value); context.pushBlock(op.block); this.buildBlockRefList(context, block_value.refList); for (const v of block_value.values) this.buildValue(v, context); context.popBlockVars(block_value); context.popBlock(); break; } case SemanticsValueKind.BLOCK_BRANCH: { const branch = value as BlockBranchValue; const ir_target = context.getIRBlock(branch.target); if (!ir_target) { throw Error(`IR Need Target`); } const op = IRCode.NewBranch(ir_target); context.pushCode(op); break; } case SemanticsValueKind.BLOCK_BRANCH_IF: { const branch = value as BlockBranchIfValue; const ir_taget = context.getIRBlock(branch.target.target); if (!ir_taget) { throw Error(`IR Need Target`); } const op = IRCode.NewBranchIf(ir_taget, branch.trueBranch); context.decTemp(); // pop the result of stack context.pushCode(op); break; } case SemanticsValueKind.THIS: context.pushCode( IRCode.LoadThis(this.funcNode!.thisClassType!), ); context.incTemp(); break; case SemanticsValueKind.SUPER: context.pushCode( IRCode.LoadThis(this.funcNode!.thisClassType!.super!), ); context.incTemp(); // TODO break; case SemanticsValueKind.LITERAL: context.pushCode(IRCode.LoadConst(value as LiteralValue)); context.incTemp(); break; case SemanticsValueKind.PARAM_VAR: /* falls through */ case SemanticsValueKind.CLOSURE_VAR: /* falls through */ case SemanticsValueKind.CLOSURE_CONST: /* falls through */ case SemanticsValueKind.LOCAL_VAR: /* falls through */ case SemanticsValueKind.LOCAL_CONST: { const v = value as VarValue; if (v.ref instanceof VarDeclareNode) { const var_decl = v.ref as VarDeclareNode; this.buildVar(var_decl, context, true); if (var_decl.isRef()) context.pushCode(IRCode.ReadRef(var_decl.type)); context.incTemp(); } break; } case SemanticsValueKind.GLOBAL_VAR: /* falls through */ case SemanticsValueKind.GLOBAL_CONST: { const varValue = value as VarValue; if ( value.kind == SemanticsValueKind.GLOBAL_CONST && varValue.ref instanceof FunctionDeclareNode ) { const func = varValue.ref as FunctionDeclareNode; context.pushCode( IRCode.LoadFunction( context.getFunctionIndex(func, S.LOAD), func.funcType.returnType, ), ); } else if (varValue.ref instanceof ObjectType) { const obj_type = varValue.ref as ObjectType; if (obj_type.isClassObject()) { // load class context.pushCode( IRCode.LoadClass( context.getClassIndex( obj_type.instanceType!, S.LOAD, ), obj_type, ), ); } } else if (varValue.ref instanceof VarDeclareNode) { const var_decl = varValue.ref as VarDeclareNode; context.pushCode( IRCode.LoadGlobal( context.getGlobalVarIndex(var_decl, S.LOAD), varValue.type, ), ); if (var_decl.isRef()) context.pushCode(IRCode.ReadRef(var_decl.type)); } else { throw Error(`unkown var ${varValue}`); break; } context.incTemp(); break; } case SemanticsValueKind.BINARY_EXPR: this.buildBinaryExprValue(value as BinaryExprValue, context); break; /*case SemanticsValueKind.POST_UNARY_EXPR: context.pushCode(IRCode.LoadPostUnaryExpr(value as PostUnaryExprValue)); break; case SemanticsValueKind.PRE_UNARY_EXPR: context.pushCode(IRCode.LoadPreUnaryExpr(value as PreUnaryExprValue)); break;*/ case SemanticsValueKind.FUNCTION_CALL: { const func = value as FunctionCallValue; this.buildValue(func.func, context); context.incTemp(); if (func.parameters) { for (const p of func.parameters) { this.buildValue(p, context); } } const param_count = func.parameters ? func.parameters.length : 0; context.pushCode( IRCode.NewCall(func.type, param_count, func.typeArguments), ); context.decTemp(param_count); break; } case SemanticsValueKind.CLOSURE_CALL: break; case SemanticsValueKind.STRING_INDEX_GET: /* falls through */ case SemanticsValueKind.ARRAY_INDEX_GET: /* falls through */ case SemanticsValueKind.OBJECT_INDEX_GET: /* falls through */ case SemanticsValueKind.OBJECT_KEY_GET: { const element = value as ElementGetValue; this.buildValue(element.index, context); this.buildValue(element.owner, context); if (value.kind == SemanticsValueKind.STRING_INDEX_GET) context.pushCode(IRCode.NewStringIndexGet()); else if (value.kind == SemanticsValueKind.ARRAY_INDEX_GET) context.pushCode(IRCode.NewArrayIndexGet(element.type)); else if (value.kind == SemanticsValueKind.OBJECT_INDEX_GET) context.pushCode(IRCode.NewObjectIndexGet(element.type)); else context.pushCode(IRCode.NewObjectKeyGet(element.type)); context.decTemp(); break; } case SemanticsValueKind.STRING_INDEX_SET: /* falls through */ case SemanticsValueKind.ARRAY_INDEX_SET: /* falls through */ case SemanticsValueKind.OBJECT_INDEX_SET: /* falls through */ case SemanticsValueKind.OBJECT_KEY_SET: { const element = value as ElementSetValue; this.buildValue(element.owner, context); this.buildValue(element.index, context); if ( element.opKind && element.opKind != ts.SyntaxKind.EqualsToken ) { // a[b] += expr , a.b -= expr ... // load a [a] // load b [a,b] // ------ // dup -1 [a,b,a] // dup -1 [a,b,a,b] // element_get [a,b, ] // load expr [a,b, , ] // add [a,b, ] // ---------------- // element_set [] context.pushCode( IRCode.NewDupStackValue(-1, element.owner.type), ); context.pushCode( IRCode.NewDupStackValue(-1, element.index.type), ); context.incTemp(2); this.buildValue(element.value!, context); context.pushCode( IRCode.NewBinaryOp(element.opKind!, element.type), ); } else { // a[b] = expr // load a [a] // load b [a,b] // ------ // load expr [a,b, expr] // ------ // element_set [] this.buildValue(element.value!, context); } if (value.kind == SemanticsValueKind.STRING_INDEX_SET) context.pushCode(IRCode.NewStringIndexSet()); else if (value.kind == SemanticsValueKind.ARRAY_INDEX_SET) context.pushCode(IRCode.NewArrayIndexSet(element.type)); else if (value.kind == SemanticsValueKind.OBJECT_INDEX_SET) context.pushCode(IRCode.NewObjectIndexSet(element.type)); else context.pushCode(IRCode.NewObjectKeySet(element.type)); context.decTemp(2); break; } case SemanticsValueKind.NEW_CONSTRCTOR_OBJECT: { const new_object = value as NewConstructorObjectValue; const obj_type = new_object.objectType; const clazz_idx = context.getClassIndex(obj_type, S.NEW); context.pushCode(IRCode.NewObject(clazz_idx, obj_type)); context.incTemp(); for (const p of new_object.parameters) { this.buildValue(p, context); } context.pushCode( IRCode.NewConstructorCall( new_object.type, new_object.parameters.length, new_object.typeArguments, ), ); context.decTemp(new_object.parameters.length); break; } case SemanticsValueKind.NEW_LITERAL_OBJECT: { const nlo = value as NewLiteralObjectValue; const obj_type = nlo.objectType; const meta = obj_type.meta; context.pushCode( IRCode.NewObject( context.getClassIndex(obj_type, S.NEW), obj_type, ), ); context.incTemp(); for (let i = 0; i < nlo.initValues.length; i++) { const f = nlo.initValues[i]; const member = meta.members[i]; if (f) { // f may be optional, it's would be null context.pushCode(IRCode.NewDupStackValue(0, f.type)); this.buildValue(f, context); context.pushCode( IRCode.NewSetOffset(member.index, f.type), ); } } context.decTemp(nlo.initValues.length); break; } case SemanticsValueKind.NEW_FROM_CLASS_OBJECT: { const new_obj = value as NewFromClassObjectValue; this.buildValue(new_obj.clazz, context); context.pushCode(IRCode.NewDynamic(new_obj.type)); for (let i = 0; i < new_obj.parameters.length; i++) { this.buildValue(new_obj.parameters[i], context); } context.pushCode( IRCode.NewConstructorCall( new_obj.type, new_obj.parameters.length, new_obj.typeArguments, ), ); context.decTemp(new_obj.parameters.length); break; } case SemanticsValueKind.NEW_LITERAL_ARRAY: { const new_literal = value as NewLiteralArrayValue; for (let i = 0; i < new_literal.initValues.length; i++) { this.buildValue(new_literal.initValues[i], context); } context.pushCode( IRCode.NewArrayParameters( new_literal.initValues.length, new_literal.type, ), ); context.decTemp(new_literal.initValues.length - 1); break; } case SemanticsValueKind.NEW_ARRAY: { const new_array = value as NewArrayValue; for (let i = 0; i < new_array.parameters.length; i++) { this.buildValue(new_array.parameters[i], context); } context.pushCode( IRCode.NewArrayParameters( new_array.parameters.length, new_array.type, ), ); context.decTemp(new_array.parameters.length - 1); break; } case SemanticsValueKind.NEW_ARRAY_LEN: { const new_array_len = value as NewArrayLenValue; this.buildValue(new_array_len.len, context); context.pushCode(IRCode.NewArrayLength(new_array_len.type)); context.incTemp(); break; } case SemanticsValueKind.NEW_CLOSURE_FUNCTION: { const new_closure = value as NewClosureFunction; // new_closure func_idx // load_val init0 // init_closure_value 0 // ... const func_idx = context.getFunctionIndex( new_closure.funcNode, S.NEW, ); context.pushCode(IRCode.NewClosure(func_idx, new_closure.type)); context.incTemp(); if (new_closure.closureInitList) { const init_list = new_closure.closureInitList!; for (let i = 0; i < init_list.length; i++) { this.buildVar( init_list[i].ref as VarDeclareNode, context, true, ); context.pushCode( IRCode.InitClosureValue(i, init_list[i].type), ); } } break; } case SemanticsValueKind.VALUE_CAST_VALUE: /* falls through */ case SemanticsValueKind.ANY_CAST_VALUE: /* falls through */ case SemanticsValueKind.VALUE_CAST_ANY: { const cv = value as CastValue; this.buildValue(cv.value, context); //context.pushCode(IRCode.NewValueCast(cv)); break; } case SemanticsValueKind.ANY_CAST_OBJECT: /* falls through */ case SemanticsValueKind.OBJECT_CAST_ANY: { const cv = value as CastValue; this.buildValue(cv.value, context); // TODO //context.pushCode(IRCode.NewValueCast(cv)); break; } case SemanticsValueKind.OBJECT_CAST_OBJECT: { const cast_value = value as CastValue; this.buildValue(cast_value.value, context); if (cast_value.shape) { const shape_idx = context.getShapeIndex(cast_value.shape); context.pushCode( IRCode.NewBindShape(shape_idx, cast_value.type), ); } else { const obj_type = cast_value.type as ObjectType; const meta_idx = context.getMetaIndex(obj_type.meta); context.pushCode(IRCode.NewBuildShape(meta_idx, obj_type)); } context.incTemp(); break; } case SemanticsValueKind.DIRECT_GETTER: { /* sp[0] = this sp[-1] = func */ const direct_get = value as DirectGetterValue; const getter = direct_get.getter as VarValue; if (!(getter.ref instanceof FunctionDeclareNode)) { throw Error(`DirectGetter should has a function ${getter}`); } const func = getter.ref as FunctionDeclareNode; const func_idx = context.getFunctionIndex(func, S.CALL); // func context.pushCode( IRCode.LoadFunction(func_idx, direct_get.type), ); context.incTemp(); // this this.buildValue(direct_get.owner, context); // call context.pushCode(IRCode.NewMethodCall(direct_get.type, 0)); context.decTemp(); // pop this, func, push result break; } case SemanticsValueKind.DIRECT_SETTER: { /* sp[0] = value sp[-1] = this sp[-2] = func */ const direct_set = value as DirectSetterValue; const setter = direct_set.setter as VarValue; if (!(setter.ref instanceof FunctionDeclareNode)) { throw Error(`DirectGetter should has a function ${setter}`); } // load_function setter const func = setter.ref as FunctionDeclareNode; const func_idx = context.getFunctionIndex(func, S.CALL); // func context.pushCode( IRCode.LoadFunction(func_idx, direct_set.type), ); context.incTemp(); // push this this.buildValue(direct_set.owner, context); if ( direct_set.opKind && direct_set.opKind != ts.SyntaxKind.EqualsToken ) { // a.b += value // load_function getter // dup this // call getter // push value // add const getter = direct_set.getter; if (!getter || !(getter instanceof VarValue)) { throw Error( `DirectSetter ${ ts.SyntaxKind[direct_set.opKind] } need a getter`, ); } const ref = (getter as VarValue).ref; if (!(ref instanceof FunctionDeclareNode)) { throw Error( `DirectSetter need function getter ${getter}`, ); } const func = ref as FunctionDeclareNode; const getter_idx = context.getFunctionIndex(func, S.CALL); context.pushCode( IRCode.LoadFunction(getter_idx, direct_set.type), ); context.pushCode( IRCode.NewDupStackValue(-1, direct_set.owner.type), ); // call getter context.pushCode(IRCode.NewMethodCall(direct_set.type, 0)); context.incTemp(); this.buildValue(direct_set.value!, context); context.pushCode( IRCode.NewBinaryOp(direct_set.opKind!, direct_set.type), ); } else { // push value this.buildValue(direct_set.value!, context); } // call context.pushCode(IRCode.NewMethodCall(Primitive.Void, 0)); context.decTemp(3); break; } case SemanticsValueKind.DIRECT_CALL: { const direct_call = value as DirectCallValue; const method = direct_call.method as VarValue; if (!(method.ref instanceof FunctionDeclareNode)) { throw Error(`Cannot find the function ${method}`); } const func = method.ref as FunctionDeclareNode; const func_idx = context.getFunctionIndex(func, S.CALL); context.pushCode( IRCode.LoadFunction(func_idx, func.funcType.returnType), ); context.incTemp(); this.buildValue(direct_call.owner, context); this.buildMethodCall(direct_call, context); break; } case SemanticsValueKind.OFFSET_GET: { const offset_get = value as OffsetGetValue; this.buildValue(offset_get.owner, context); context.pushCode( IRCode.NewGetOffset(offset_get.index, offset_get.type), ); break; } case SemanticsValueKind.OFFSET_GETTER: { const offset_get = value as OffsetGetterValue; this.buildValue(offset_get.owner, context); context.pushCode( IRCode.NewDupStackValue(0, offset_get.owner.type), ); context.incTemp(); // get the function context.pushCode( IRCode.NewGetOffset(offset_get.index, offset_get.type), ); context.pushCode(IRCode.NewSwap()); // call context.pushCode(IRCode.NewMethodCall(offset_get.type, 0)); context.decTemp(); break; } case SemanticsValueKind.OFFSET_SET: { const offset_set = value as OffsetSetValue; // push owner this.buildValue(offset_set.owner, context); if ( offset_set.opKind && offset_set.opKind != ts.SyntaxKind.EqualsToken ) { // dup 0 // get_offset index // push value // op context.pushCode( IRCode.NewDupStackValue(0, offset_set.owner.type), ); context.pushCode( IRCode.NewGetOffset(offset_set.index, offset_set.type), ); context.incTemp(); this.buildValue(offset_set.value!, context); context.pushCode( IRCode.NewBinaryOp(offset_set.opKind, offset_set.type), ); } else { // push value this.buildValue(offset_set.value!, context); } // get_offset index context.pushCode( IRCode.NewSetOffset(offset_set.index, offset_set.type), ); context.decTemp(2); break; } case SemanticsValueKind.OFFSET_SETTER: { const offset_set = value as OffsetSetterValue; // push this this.buildValue(offset_set.owner, context); // dup 0 context.pushCode( IRCode.NewDupStackValue(0, offset_set.owner.type), ); context.incTemp(); // get_offset index context.pushCode( IRCode.NewGetOffset(offset_set.index, offset_set.type), ); // swap this/setter context.pushCode(IRCode.NewSwap()); // sp[0] = this // sp[-1] = setter context.incTemp(2); if ( offset_set.opKind && offset_set.opKind != ts.SyntaxKind.EqualsToken ) { if (!offset_set.getterIndex) { throw Error( `OffsetSetterValue's getterIndex is null ${offset_set}`, ); } // dup this // get_offset getterIndex // dup this // method_call 0 // push value // op context.pushCode( IRCode.NewDupStackValue(0, offset_set.owner.type), ); context.pushCode( // TODO create function type IRCode.NewGetOffset( offset_set.getterIndex!, offset_set.type, ), ); context.pushCode( IRCode.NewDupStackValue(-1, offset_set.owner.type), ); context.pushCode(IRCode.NewMethodCall(offset_set.type, 0)); context.incTemp(); this.buildValue(offset_set.value!, context); context.pushCode( IRCode.NewBinaryOp(offset_set.opKind!, offset_set.type), ); } else { // push value this.buildValue(offset_set.value!, context); } // call setter(this, value) context.pushCode(IRCode.NewMethodCall(offset_set.type, 1)); context.decTemp(3); break; } case SemanticsValueKind.OFFSET_CALL: { const offset_call = value as OffsetCallValue; // push this this.buildValue(offset_call.owner, context); // dup 0 context.pushCode( IRCode.NewDupStackValue(0, offset_call.owner.type), ); context.incTemp(); // get method context.pushCode( IRCode.NewGetOffset( offset_call.index, offset_call.funcType, ), ); // swap this/method context.pushCode(IRCode.NewSwap()); this.buildMethodCall(offset_call, context); break; } case SemanticsValueKind.VTABLE_GET: { const vtable_get = value as VTableGetValue; // get this this.buildValue(vtable_get.owner, context); context.pushCode( IRCode.NewGetVTable(vtable_get.index, vtable_get.type), ); break; } case SemanticsValueKind.VTABLE_SET: { const vtable_set = value as VTableSetValue; // get this this.buildValue(vtable_set.owner, context); if ( vtable_set.opKind && vtable_set.opKind != ts.SyntaxKind.EqualsToken ) { // dup this // vtable_get index // push value // op context.pushCode( IRCode.NewDupStackValue(0, vtable_set.owner.type), ); context.pushCode( IRCode.NewGetVTable(vtable_set.index, vtable_set.type), ); context.incTemp(); this.buildValue(vtable_set.value!, context); context.pushCode( IRCode.NewBinaryOp(vtable_set.opKind!, vtable_set.type), ); } else { // get value this.buildValue(vtable_set.value!, context); } context.pushCode( IRCode.NewSetVTable(vtable_set.index, vtable_set.type), ); context.decTemp(); break; } case SemanticsValueKind.VTABLE_CALL: { const vtable_call = value as VTableCallValue; // get this this.buildValue(vtable_call.owner, context); const params = vtable_call.parameters; let param_count = 0; if (params) { for (const p of params) this.buildValue(p, context); param_count = params.length; } context.pushCode( IRCode.NewVTableCall( vtable_call.index, param_count, vtable_call.type, ), ); context.decTemp(param_count); break; } case SemanticsValueKind.SHAPE_GET: { const shape_get = value as ShapeGetValue; // get this this.buildValue(shape_get.owner, context); context.pushCode( IRCode.NewGetShape(shape_get.index, shape_get.type), ); break; } case SemanticsValueKind.SHAPE_SET: { const shape_set = value as ShapeSetValue; // get this this.buildValue(shape_set.owner, context); if ( shape_set.opKind && shape_set.opKind != ts.SyntaxKind.EqualsToken ) { // dup this // shape_get index // push value // op context.pushCode( IRCode.NewDupStackValue(0, shape_set.owner.type), ); context.pushCode( IRCode.NewGetShape(shape_set.index, shape_set.type), ); context.incTemp(); this.buildValue(shape_set.value!, context); context.pushCode( IRCode.NewBinaryOp(shape_set.opKind!, shape_set.type), ); } else { // get value this.buildValue(shape_set.value!, context); } context.pushCode( IRCode.NewSetShape(shape_set.index, shape_set.type), ); context.decTemp(); break; } case SemanticsValueKind.SHAPE_CALL: { const shape_call = value as ShapeCallValue; // get this this.buildValue(shape_call.owner, context); const params = shape_call.parameters; let param_count = 0; if (params) { for (const p of params) this.buildValue(p, context); param_count = params.length; } context.pushCode( IRCode.NewShapeCall( shape_call.index, param_count, shape_call.type, ), ); context.decTemp(param_count); break; } case SemanticsValueKind.DYNAMIC_GET: { const dyn_get = value as DynamicGetValue; this.buildValue(dyn_get.owner, context); const name_offset = context.addString(dyn_get.name); context.pushCode(IRCode.NewGetDynamic(name_offset)); break; } case SemanticsValueKind.DYNAMIC_SET: { const dyn_set = value as DynamicSetValue; this.buildValue(dyn_set.owner, context); const name_offset = context.addString(dyn_set.name); if ( dyn_set.opKind && dyn_set.opKind != ts.SyntaxKind.EqualsToken ) { // dup owner // get_dynamic name_offset // push value // op context.pushCode( IRCode.NewDupStackValue(0, dyn_set.owner.type), ); context.pushCode(IRCode.NewGetDynamic(name_offset)); context.incTemp(); this.buildValue(dyn_set.value!, context); context.pushCode( IRCode.NewBinaryOp(dyn_set.opKind!, Primitive.Any), ); } else { this.buildValue(dyn_set.value!, context); } context.pushCode(IRCode.NewSetDynamic(name_offset)); break; } case SemanticsValueKind.DYNAMIC_CALL: { const dyn_call = value as DynamicCallValue; this.buildValue(dyn_call.owner, context); const name_offset = context.addString(dyn_call.name); const params = dyn_call.parameters; let param_count = 0; if (params) { for (const p of params) this.buildValue(p, context); param_count = params.length; } context.pushCode( IRCode.NewDynamicCall(name_offset, param_count), ); context.decTemp(param_count); break; } case SemanticsValueKind.RET: { const ret_value = value as ReturnValue; if (ret_value.expr) { this.buildValue(ret_value.expr, context); } context.pushCode( IRCode.NewReturn( ret_value.expr ? ret_value.expr!.type : undefined, ), ); break; } } } private buildMethodCall(func: FunctionCallBaseValue, context: Context) { let param_count = 0; const parameters = func.parameters; if (parameters) { for (const p of parameters) this.buildValue(p, context); param_count = parameters.length; } context.pushCode( IRCode.NewMethodCall( func.funcType.returnType, param_count, func.typeArguments, ), ); context.decTemp(param_count + 1); // params this, func, push result } private buildSaveVar(v: VarValue, context: Context) { if (!(v.ref instanceof VarDeclareNode)) throw Error(`${v} must be a var`); const var_decl = v.ref as VarDeclareNode; switch (v.kind) { case SemanticsValueKind.LOCAL_CONST: // TODO /* falls through */ case SemanticsValueKind.LOCAL_VAR: /* falls through */ case SemanticsValueKind.PARAM_VAR: /* falls through */ case SemanticsValueKind.CLOSURE_CONST: /* falls through */ case SemanticsValueKind.CLOSURE_VAR: if (var_decl.isRef()) { this.buildVar(var_decl, context, true); context.pushCode(IRCode.WriteRef(var_decl.type)); } else { this.buildVar(var_decl, context, false); } break; case SemanticsValueKind.GLOBAL_CONST: // TODO /* falls through */ case SemanticsValueKind.GLOBAL_VAR: if (var_decl.isRef()) { context.pushCode( IRCode.LoadGlobal( context.getGlobalVarIndex(var_decl, S.LOAD), v.type, ), ); context.pushCode(IRCode.WriteRef(var_decl.type)); } else { context.pushCode( IRCode.SaveGlobal( context.getGlobalVarIndex(var_decl, S.SAVE), v.type, ), ); } break; default: throw Error(`var ${v} cannot be saved`); } context.decTemp(); } private buildBinaryExprValue(bv: BinaryExprValue, context: Context) { if (bv.opKind == ts.SyntaxKind.EqualsToken) { this.buildValue(bv.right, context); if (bv.left instanceof VarValue) { this.buildSaveVar(bv.left as VarValue, context); } else { throw Error(`error unkown binary express left ${bv.left}`); } return; // don't check isEqualOperator } else if (isEqualOperator(bv.opKind)) { // a += b // sp[0] = b // sp[-1] = a // if (bv.left instanceof VarValue) { this.buildValue(bv.left, context); this.buildValue(bv.right, context); context.pushCode(IRCode.NewBinaryOp(bv.opKind, bv.type)); context.decTemp(); this.buildSaveVar(bv.left as VarValue, context); } else { throw Error( `error unkown binary express left ${bv.left} in binary ${bv}`, ); } } else { // a + b // sp[0] = b // sp[-1] = a this.buildValue(bv.left, context); this.buildValue(bv.right, context); context.pushCode(IRCode.NewBinaryOp(bv.opKind, bv.type)); context.decTemp(); // pop the top[0], top[-1]; } } private pushValue(opcode: IRCode) { this.codes.push(opcode); } } ================================================ FILE: src/semantics/ir/ir_context.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { MemberType, ObjectDescription, ObjectDescriptionType, MemberDescription, ShapeAccessor, Shape, ShapeMember, ShapeMethod, ShapeField, Value, } from '../runtime.js'; import { SemanticsKind, SemanticsNode, ModuleNode, FunctionDeclareNode, VarDeclareNode, BlockNode, BasicBlockNode, IfNode, ForNode, ForInNode, ForOfNode, WhileNode, ReturnNode, ExternModuleManager, ExternModule, ExternType, ExternTypeKind, ExternItem, } from '../semantics_nodes.js'; import { ValueType, ObjectType, ValueTypeKind } from '../value_types.js'; import { IRModule, RuntimeData, RuntimeExternModule, RuntimeExternItem, RuntimeExternModules, } from './irmodule.js'; export class StatisticsInfo { public loadCount = 0; public saveCount = 0; public newCount = 0; public callCount = 0; } export enum S { LOAD, NEW, SAVE, CALL, IMPORT_EXPORT, } export type StatisticsObject = | VarDeclareNode | FunctionDeclareNode | ObjectType | ObjectDescription | Shape; function buildMapAsArray(map: Map): T[] { const arr = new Array(map.size); map.forEach((idx, t) => { arr[idx - 1] = t; }); return arr; } export interface ExternItemInfo { module: RuntimeExternModule; item: RuntimeExternItem; } export class GlobalContext { // globalVar private _globalVars = new Map(); // globalFunctions private _globalFunctions = new Map(); // globalClass (Class & Literal) private _globalClasses = new Map(); // global meta index private _globalMetas = new Map(); // global static shape private _globalStaticShapes = new Map(); // statistics private _globalStatiscsInfo = new Map(); private _imports?: RuntimeExternModules; private _exports?: RuntimeExternModules; private _importMap = new Map(); private _exportMap = new Map(); constructor(public readonly module: IRModule) { this.processVars(); this.processFunctions(); this.processClasses(); this.processMetaShapes(); this.processImportsExports(); } processVars() { const vars = this.module.module.globalVars; let i = 1; // index start 1 for (const v of vars) { this._globalVars.set(v, i++); } } processFunctions() { const funcs = this.module.module.functions; let i = 1; for (const f of funcs) { this._globalFunctions.set(f, i++); } } processClasses() { const types = this.module.module.namedTypes; let i = 1; types.forEach((vt, _) => { if (vt.kind == ValueTypeKind.OBJECT) { const clazz = vt as ObjectType; this._globalClasses.set(clazz, i++); } }); } processMetaShapes() { let meta_idx = 1; let shape_idx = 1; const metas = this.module.module.objectDescriptions; for (const meta of metas) { this._globalMetas.set(meta, meta_idx++); if (meta.originShape) { this._globalStaticShapes.set(meta.originShape, shape_idx++); } if (meta.thisShape && meta.thisShape !== meta.originShape) { this._globalStaticShapes.set(meta.thisShape, shape_idx++); } meta.compatibleShapes.forEach((s, m) => { if (!this._globalStaticShapes.has(s)) { this._globalStaticShapes.set(s, shape_idx++); } }); } } processImportsExports() { this._imports = this.buildExternModules( this.module.module.imports, this._importMap, ); this._exports = this.buildExternModules( this.module.module.exports, this._exportMap, ); } getVarIndex(decl: VarDeclareNode, s: S = S.LOAD): number { const idx = this._globalVars.get(decl); if (!idx) { throw Error(`global ${decl} is not exist`); } this.updateStatistics(decl, s); return idx! - 1; } getFunctionIndex(func: FunctionDeclareNode, s: S = S.CALL): number { const idx = this._globalFunctions.get(func); if (!idx) { throw Error(`global function ${func} is not exist`); } this.updateStatistics(func, s); return idx! - 1; } getClassIndex(clazz: ObjectType, s: S = S.NEW): number { const idx = this._globalClasses.get(clazz); if (!idx) throw Error(`global class ${clazz} is not exist`); this.updateStatistics(clazz, s); return idx! - 1; } getMetaIndex(meta: ObjectDescription): number { const idx = this._globalMetas.get(meta); if (!idx) throw Error(`global meta ${meta.name} is not exist`); this.updateStatistics(meta, S.LOAD); return idx! - 1; } getShapeIndex(shape: Shape): number { const idx = this._globalStaticShapes.get(shape.genericShape); if (!idx) throw Error(`global shape ${shape.meta.name} is not exist`); this.updateStatistics(shape, S.LOAD); return idx! - 1; } getImportInfo(type: ExternType): ExternItemInfo | undefined { return this._importMap.get(type); } updateStatistics(key: StatisticsObject, s: S) { let info = this._globalStatiscsInfo.get(key); if (!info) { info = new StatisticsInfo(); this._globalStatiscsInfo.set(key, info!); } switch (s) { case S.LOAD: info!.loadCount++; break; case S.SAVE: info!.saveCount++; break; case S.NEW: info!.newCount++; break; case S.CALL: info!.callCount++; break; } } finishBuild() { this.buildRuntimeData(); } private buildRuntimeData() { this.module.runtimeData = new RuntimeData( this.module.dataPool, buildMapAsArray(this._globalVars), buildMapAsArray(this._globalFunctions), buildMapAsArray(this._globalClasses), buildMapAsArray(this._globalMetas), buildMapAsArray(this._globalStaticShapes), this._globalStatiscsInfo, this._imports!, this._exports!, ); this.module.runtimeData!.build(this); } private buildExternModules( mod_mgr: ExternModuleManager, modMap: Map, ): RuntimeExternModules { const dataPool = this.module.dataPool; const extern_modules: RuntimeExternModule[] = []; for (const mod of mod_mgr.modules) { const items: RuntimeExternItem[] = []; const module = new RuntimeExternModule( dataPool.addString(mod.name), items, extern_modules.length, ); for (const it of mod.items) { const item = new RuntimeExternItem( dataPool.addString(it.name), it.kind, this.findExternItemIndex(it.kind, it.type), items.length, ); items.push(item); modMap.set(it.type, { module, item }); } extern_modules.push(module); } return new RuntimeExternModules(extern_modules); } private findExternItemIndex( kind: ExternTypeKind, type: ExternType, ): number { switch (kind) { case ExternTypeKind.FUNCTION: return this.getFunctionIndex( type as FunctionDeclareNode, S.IMPORT_EXPORT, ); case ExternTypeKind.CLASS: return this.getClassIndex( (type as ObjectType).instanceType!, S.IMPORT_EXPORT, ); case ExternTypeKind.VAR: return this.getVarIndex( type as VarDeclareNode, S.IMPORT_EXPORT, ); case ExternTypeKind.ENUM: // TODO get enum reflection index return -1; } return -1; } } ================================================ FILE: src/semantics/ir/ircode.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import ts from 'typescript'; import { SemanticsValue, SemanticsValueKind, VarValue, LiteralValue, BinaryExprValue, ElementGetValue, ElementSetValue, CastValue, BlockValue, ValueBinaryOperator, } from '../value.js'; import { ValueType, ValueTypeKind, ObjectType, Primitive, UnionType, TypeParameterType, } from '../value_types.js'; export enum IRCodeKind { UNKNOWN, /* opcode */ LOAD_CONST, LOAD_STRING, LOAD_PARAM, LOAD_LOCAL, LOAD_GLOBAL, LOAD_CLOSURE, LOAD_FUNCTION, LOAD_CLASS, LOAD_UNDEFINED, LOAD_NULL, SAVE_PARAM, SAVE_LOCAL, SAVE_GLOBAL, SAVE_CLOSURE, DROP, DUP, SWAP, // swap the two value NEW_REF, READ_REF, WRITE_REF, ADD, SUB, MULT, DIV, MOD, // .... ASSIGN, ASSIGN_ADD, ASSIGN_SUB, ASSIGN_MULT, ASSIGN_DIV, ASSIGN_MOD, // .... LT, LTE, GT, GTE, EQ, EQ2, // === NEQ, NEQ2, // !== AND, OR, NOT, INC, DEC, IS_NULL, CALL, METHOD_CALL, CONSTRUCTOR_CALL, GET_OFFSET, SET_OFFSET, GET_VTABLE, SET_VTABLE, VTABLE_CALL, GET_SHAPE, SET_SHAPE, SHAPE_CALL, GET_DYNAMIC, SET_DYNAMIC, DYNAMIC_CALL, STRING_INDEX_GET, STRING_INDEX_SET, ARRAY_INDEX_GET, ARRAY_INDEX_SET, OBJECT_INDEX_GET, OBJECT_INDEX_SET, OBJECT_KEY_GET, OBJECT_KEY_SET, NEW_OBJECT, NEW_DYNAMIC, NEW_CLOSURE, NEW_ARRAY_PARAMS, NEW_ARRAY_LENGTH, INIT_CLOSURE_VALUE, INSTANCE_OF, INSTANCE_OF_DYNAMIC, BUILD_SHAPE, BIND_SHAPE, UNBOUND_SHAPE, GET_KEY_ITER, GET_VALUE_ITER, NEXT_ITER, RETURN, VALUE_CAST, STATIC_CAST, DYN_CAST, PUSH_TRY, POP_TRY, THROW, FINALLY_END, AWAIT, YIELD, BLOCK, BRANCH, BRANCH_TRUE, BRANCH_FALSE, // for import function IMPORT_FUNCTION, } export enum IRCodeValueType { VOID, NATIVE, NULL, UNDEFINED, INT, INT8, UINT8, INT16, UINT16, INT32, UINT32, INT64, UINT64, F32, F64, BOOLEAN, RAW_STRING, STRING, ANY, REFERENCE, OBJECT, INTERFACE, ENUM, GENERIC, } export function isObjectIRValue(type: IRCodeValueType): boolean { return ( type == IRCodeValueType.STRING || type == IRCodeValueType.OBJECT || type == IRCodeValueType.INTERFACE ); } export function isIntIRValue(type: IRCodeValueType): boolean { return type >= IRCodeValueType.INT && type <= IRCodeValueType.UINT64; } export function isStringIRValue(type: IRCodeValueType): boolean { return type == IRCodeValueType.RAW_STRING || type == IRCodeValueType.STRING; } export function isNumberIRValue(type: IRCodeValueType): boolean { return type == IRCodeValueType.F32 || type == IRCodeValueType.F64; } function GetIRCodeValueType(value: SemanticsValue): IRCodeValueType { return GetIRCodeValueTypeFromType(value.type); } function GetElementKind(start: IRCodeKind, type: ValueTypeKind): IRCodeKind { if (type == ValueTypeKind.INT || type == ValueTypeKind.NUMBER) return start; if (type == ValueTypeKind.RAW_STRING || type == ValueTypeKind.STRING) return start + 1; return start + 2; } function GetIRCodeValueTypeFromType(type: ValueType): IRCodeValueType { switch (type.kind) { case ValueTypeKind.VOID: case ValueTypeKind.UNDEFINED: return IRCodeValueType.UNDEFINED; case ValueTypeKind.NEVER: case ValueTypeKind.NULL: return IRCodeValueType.NULL; case ValueTypeKind.INT: return IRCodeValueType.INT; case ValueTypeKind.NUMBER: return IRCodeValueType.F64; case ValueTypeKind.BOOLEAN: return IRCodeValueType.BOOLEAN; case ValueTypeKind.RAW_STRING: return IRCodeValueType.RAW_STRING; case ValueTypeKind.STRING: return IRCodeValueType.STRING; case ValueTypeKind.ANY: return IRCodeValueType.ANY; case ValueTypeKind.ENUM: return IRCodeValueType.ENUM; case ValueTypeKind.INTERFACE: return IRCodeValueType.INTERFACE; case ValueTypeKind.TYPE_PARAMETER: return GetIRCodeValueTypeFromType( (type as TypeParameterType).wideType, ); case ValueTypeKind.UNION: return GetIRCodeValueTypeFromType((type as UnionType).wideType); case ValueTypeKind.GENERIC: return IRCodeValueType.GENERIC; default: return IRCodeValueType.OBJECT; } } const labelId = 1; export class IRCode { constructor( public readonly kind: IRCodeKind, public readonly type: IRCodeValueType, public readonly valueType: ValueType, private oph: any = undefined, ) {} private _typeArguments?: ValueType[]; get index(): number { return this.oph as number; } get offset(): number { return this.oph as number; } get vtableIndex(): number { return (this.oph as number) >> 8; } get shapeIndex(): number { return (this.oph as number) >> 8; } get dynamicIndex(): number { return (this.oph as number) >> 8; } get paramCount(): number { return (this.oph as number) & 0xff; } get member(): string { return this.oph as string; } get value(): any { return this.oph; } get labelId(): number { return this.oph as number; } get fromType(): IRCodeValueType { return this.oph as IRCodeValueType; } get block(): IRBlock { return this.oph as IRBlock; } setTypeArguments(typeArgs: ValueType[]) { this._typeArguments = typeArgs; } get typeArguments(): ValueType[] | undefined { return this._typeArguments; } toString(): string { const op = this.oph ? `${this.oph}` : ''; return `[${IRCodeKind[this.kind]} ${IRCodeValueType[this.type]} ${op}]`; } static LoadConst(value: LiteralValue): IRCode { return new IRCode( IRCodeKind.LOAD_CONST, GetIRCodeValueType(value), value.type, value.value, ); } static LoadString(offset: number): IRCode { return new IRCode( IRCodeKind.LOAD_STRING, IRCodeValueType.INT32, Primitive.RawString, offset, ); } static NewUndefined(): IRCode { return new IRCode( IRCodeKind.LOAD_UNDEFINED, IRCodeValueType.UNDEFINED, Primitive.Undefined, ); } static NewReturn(vt?: ValueType): IRCode { return new IRCode( IRCodeKind.RETURN, vt ? GetIRCodeValueTypeFromType(vt!) : IRCodeValueType.UNDEFINED, vt ? vt : Primitive.Undefined, ); } static LoadThis(clazz_type: ObjectType): IRCode { return new IRCode( IRCodeKind.LOAD_PARAM, IRCodeValueType.OBJECT, clazz_type, 0, ); } static LoadLocal(index: number, v: ValueType): IRCode { return new IRCode( IRCodeKind.LOAD_LOCAL, GetIRCodeValueTypeFromType(v), v, index, ); } static LoadGlobal(index: number, v: ValueType): IRCode { return new IRCode( IRCodeKind.LOAD_GLOBAL, GetIRCodeValueTypeFromType(v), v, index, ); } static LoadFunction(index: number, vt: ValueType): IRCode { return new IRCode( IRCodeKind.LOAD_FUNCTION, GetIRCodeValueTypeFromType(vt), vt, index, ); } static LoadClass(index: number, obj_type: ObjectType): IRCode { return new IRCode( IRCodeKind.LOAD_CLASS, IRCodeValueType.OBJECT, obj_type, index, ); } static LoadParam(idx: number, v: ValueType): IRCode { return new IRCode( IRCodeKind.LOAD_PARAM, GetIRCodeValueTypeFromType(v), v, idx, ); } static LoadClosure(index: number, v: ValueType): IRCode { return new IRCode( IRCodeKind.LOAD_CLOSURE, GetIRCodeValueTypeFromType(v), v, index, ); } static NewRef(vt?: ValueType): IRCode { return new IRCode( IRCodeKind.NEW_REF, IRCodeValueType.REFERENCE, vt ? vt : Primitive.Any, ); } static ReadRef(vt: ValueType): IRCode { return new IRCode( IRCodeKind.READ_REF, GetIRCodeValueTypeFromType(vt), vt, ); } static WriteRef(vt: ValueType): IRCode { return new IRCode( IRCodeKind.WRITE_REF, GetIRCodeValueTypeFromType(vt), vt, ); } static NewSave(v: VarValue): IRCode { let kind = IRCodeKind.SAVE_PARAM; switch (v.kind) { case SemanticsValueKind.LOCAL_VAR: case SemanticsValueKind.LOCAL_CONST: kind = IRCodeKind.SAVE_LOCAL; break; case SemanticsValueKind.GLOBAL_VAR: case SemanticsValueKind.GLOBAL_CONST: kind = IRCodeKind.SAVE_GLOBAL; break; case SemanticsValueKind.PARAM_VAR: kind = IRCodeKind.SAVE_PARAM; break; case SemanticsValueKind.CLOSURE_VAR: //kind = IRCodeKind.SAVE_CLOSURE_REF; break; default: throw Error( `unknown the opcode: ${SemanticsValueKind[v.kind]}`, ); break; } return new IRCode(kind, GetIRCodeValueType(v), v.type, v.index); } static SaveParam(index: number, type: ValueType): IRCode { return new IRCode( IRCodeKind.SAVE_PARAM, GetIRCodeValueTypeFromType(type), type, index, ); } static SaveLocal(index: number, type: ValueType): IRCode { return new IRCode( IRCodeKind.SAVE_LOCAL, GetIRCodeValueTypeFromType(type), type, index, ); } static SaveGlobal(index: number, type: ValueType): IRCode { return new IRCode( IRCodeKind.SAVE_GLOBAL, GetIRCodeValueTypeFromType(type), type, index, ); } static SaveClosure(index: number, type: ValueType): IRCode { return new IRCode( IRCodeKind.SAVE_CLOSURE, GetIRCodeValueTypeFromType(type), type, index, ); } static NewBinaryOp(opKind: ValueBinaryOperator, type: ValueType): IRCode { let kind = IRCodeKind.ADD; switch (opKind) { case ts.SyntaxKind.PlusEqualsToken: case ts.SyntaxKind.PlusToken: kind = IRCodeKind.ADD; break; case ts.SyntaxKind.MinusEqualsToken: case ts.SyntaxKind.MinusToken: kind = IRCodeKind.SUB; break; case ts.SyntaxKind.AsteriskEqualsToken: case ts.SyntaxKind.AsteriskToken: kind = IRCodeKind.MULT; break; case ts.SyntaxKind.SlashEqualsToken: case ts.SyntaxKind.SlashToken: kind = IRCodeKind.DIV; break; case ts.SyntaxKind.PercentEqualsToken: case ts.SyntaxKind.PercentToken: kind = IRCodeKind.MOD; break; case ts.SyntaxKind.LessThanToken: kind = IRCodeKind.LT; break; case ts.SyntaxKind.LessThanEqualsToken: kind = IRCodeKind.LTE; break; case ts.SyntaxKind.GreaterThanToken: kind = IRCodeKind.GT; break; case ts.SyntaxKind.GreaterThanEqualsToken: kind = IRCodeKind.GTE; break; case ts.SyntaxKind.EqualsEqualsToken: kind = IRCodeKind.EQ; break; case ts.SyntaxKind.EqualsEqualsEqualsToken: kind = IRCodeKind.EQ2; break; case ts.SyntaxKind.ExclamationEqualsToken: kind = IRCodeKind.NEQ; break; case ts.SyntaxKind.ExclamationEqualsEqualsToken: kind = IRCodeKind.NEQ2; break; case ts.SyntaxKind.AmpersandAmpersandToken: kind = IRCodeKind.AND; break; case ts.SyntaxKind.BarBarToken: kind = IRCodeKind.OR; break; default: throw Error(`unknown binary operator ${ts.SyntaxKind[opKind]}`); } return new IRCode(kind, GetIRCodeValueTypeFromType(type), type); } static NewCall( type: ValueType, param_count: number, typeArgs?: ValueType[], ): IRCode { const code = new IRCode( IRCodeKind.CALL, GetIRCodeValueTypeFromType(type), type, param_count, ); if (typeArgs) code.setTypeArguments(typeArgs); return code; } static NewMethodCall( type: ValueType, param_count: number, typeArgs?: ValueType[], ): IRCode { const code = new IRCode( IRCodeKind.METHOD_CALL, GetIRCodeValueTypeFromType(type), type, param_count, ); if (typeArgs) code.setTypeArguments(typeArgs); return code; } static NewConstructorCall( type: ValueType, param_count: number, typeArgs?: ValueType[], ): IRCode { const code = new IRCode( IRCodeKind.CONSTRUCTOR_CALL, IRCodeValueType.UNDEFINED, type, param_count, ); if (typeArgs) code.setTypeArguments(typeArgs); return code; } static NewDupStackValue(index: number, vt: ValueType): IRCode { return new IRCode( IRCodeKind.DUP, GetIRCodeValueTypeFromType(vt), vt, index, ); } static NewSwap(): IRCode { return new IRCode(IRCodeKind.SWAP, IRCodeValueType.ANY, Primitive.Void); } static NewObject(idx: number, t: ObjectType): IRCode { return new IRCode( IRCodeKind.NEW_OBJECT, IRCodeValueType.OBJECT, t, idx, ); } static NewDynamic(vt: ValueType): IRCode { return new IRCode(IRCodeKind.NEW_DYNAMIC, IRCodeValueType.OBJECT, vt); } static NewArrayLength(vt: ValueType): IRCode { return new IRCode( IRCodeKind.NEW_ARRAY_LENGTH, IRCodeValueType.OBJECT, vt, ); } static NewArrayParameters(argc: number, vt: ValueType): IRCode { return new IRCode( IRCodeKind.NEW_ARRAY_PARAMS, IRCodeValueType.OBJECT, vt, argc, ); } static NewClosure(func_idx: number, vt: ValueType): IRCode { return new IRCode( IRCodeKind.NEW_CLOSURE, IRCodeValueType.OBJECT, vt, func_idx, ); } static InitClosureValue(idx: number, vt: ValueType): IRCode { return new IRCode( IRCodeKind.INIT_CLOSURE_VALUE, GetIRCodeValueTypeFromType(vt), vt, idx, ); } static NewStringIndexGet(): IRCode { return new IRCode( IRCodeKind.STRING_INDEX_GET, IRCodeValueType.STRING, Primitive.String, ); } static NewStringIndexSet(): IRCode { return new IRCode( IRCodeKind.STRING_INDEX_SET, IRCodeValueType.STRING, Primitive.String, ); } static NewArrayIndexGet(vt: ValueType): IRCode { return new IRCode( IRCodeKind.ARRAY_INDEX_GET, GetIRCodeValueTypeFromType(vt), vt, ); } static NewArrayIndexSet(vt: ValueType): IRCode { return new IRCode( IRCodeKind.ARRAY_INDEX_SET, GetIRCodeValueTypeFromType(vt), vt, ); } static NewObjectIndexGet(vt: ValueType): IRCode { return new IRCode( IRCodeKind.OBJECT_INDEX_GET, GetIRCodeValueTypeFromType(vt), vt, ); } static NewObjectIndexSet(vt: ValueType): IRCode { return new IRCode( IRCodeKind.OBJECT_INDEX_SET, GetIRCodeValueTypeFromType(vt), vt, ); } static NewObjectKeyGet(vt: ValueType): IRCode { return new IRCode( IRCodeKind.OBJECT_KEY_GET, GetIRCodeValueTypeFromType(vt), vt, ); } static NewObjectKeySet(vt: ValueType): IRCode { return new IRCode( IRCodeKind.OBJECT_KEY_SET, GetIRCodeValueTypeFromType(vt), vt, ); } static NewGetOffset(offset: number, vt: ValueType): IRCode { return new IRCode( IRCodeKind.GET_OFFSET, GetIRCodeValueTypeFromType(vt), vt, offset, ); } static NewSetOffset(offset: number, vt: ValueType): IRCode { return new IRCode( IRCodeKind.SET_OFFSET, GetIRCodeValueTypeFromType(vt), vt, offset, ); } static NewGetVTable(index: number, vt: ValueType): IRCode { return new IRCode( IRCodeKind.GET_VTABLE, GetIRCodeValueTypeFromType(vt), vt, index, ); } static NewSetVTable(index: number, vt: ValueType): IRCode { return new IRCode( IRCodeKind.SET_VTABLE, GetIRCodeValueTypeFromType(vt), vt, index, ); } static NewVTableCall( index: number, param_count: number, vt: ValueType, ): IRCode { return new IRCode( IRCodeKind.VTABLE_CALL, GetIRCodeValueTypeFromType(vt), vt, ((index & 0xffffff) << 8) | (param_count & 0xff), ); } static NewGetShape(index: number, vt: ValueType): IRCode { return new IRCode( IRCodeKind.GET_SHAPE, GetIRCodeValueTypeFromType(vt), vt, index, ); } static NewSetShape(index: number, vt: ValueType): IRCode { return new IRCode( IRCodeKind.SET_SHAPE, GetIRCodeValueTypeFromType(vt), vt, index, ); } static NewShapeCall( index: number, param_count: number, vt: ValueType, ): IRCode { return new IRCode( IRCodeKind.SHAPE_CALL, GetIRCodeValueTypeFromType(vt), vt, ((index & 0xffffff) << 8) | (param_count & 0xff), ); } static NewGetDynamic(index: number): IRCode { return new IRCode( IRCodeKind.GET_DYNAMIC, IRCodeValueType.ANY, Primitive.Any, index, ); } static NewSetDynamic(index: number): IRCode { return new IRCode( IRCodeKind.SET_DYNAMIC, IRCodeValueType.ANY, Primitive.Any, index, ); } static NewDynamicCall(index: number, param_count: number): IRCode { return new IRCode( IRCodeKind.DYNAMIC_CALL, IRCodeValueType.ANY, Primitive.Any, ((index & 0xffffff) << 8) | (param_count & 0xff), ); } static NewBlock(block: BlockValue): IRCode { const ir_block = new IRBlock(block.label, block.isLoop); return new IRCode( IRCodeKind.BLOCK, GetIRCodeValueTypeFromType(block.type), block.type, ir_block, ); } static NewBindShape(shape_idx: number, vt: ValueType): IRCode { return new IRCode( IRCodeKind.BIND_SHAPE, GetIRCodeValueTypeFromType(vt), vt, shape_idx, ); } static NewBuildShape(meta_idx: number, vt: ValueType): IRCode { return new IRCode( IRCodeKind.BUILD_SHAPE, GetIRCodeValueTypeFromType(vt), vt, meta_idx, ); } static NewBranch(target: IRBlock): IRCode { return new IRCode( IRCodeKind.BRANCH, IRCodeValueType.UNDEFINED, Primitive.Undefined, target, ); } static NewBranchIf(target: IRBlock, isTrue: boolean): IRCode { return new IRCode( isTrue ? IRCodeKind.BRANCH_TRUE : IRCodeKind.BRANCH_FALSE, IRCodeValueType.UNDEFINED, Primitive.Undefined, target, ); } static NewImportFunction(moduleIndex: number, funcIndex: number): IRCode { return new IRCode( IRCodeKind.IMPORT_FUNCTION, IRCodeValueType.UNDEFINED, Primitive.Undefined, ((moduleIndex & 0xffff) << 16) | (funcIndex & 0xffff), ); } } export class IRBlock { public codes: IRCode[] = []; public parent?: IRBlock; constructor( public readonly label: string, public readonly isLoop: boolean, ) {} add(code: IRCode) { this.codes.push(code); } } ================================================ FILE: src/semantics/ir/irmodule.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { IRFunction } from './function.js'; import { DataPool } from './data_pool.js'; import { Logger } from '../../log.js'; import { MemberType, ObjectDescription, ObjectDescriptionType, MemberDescription, ShapeAccessor, Shape, ShapeMember, ShapeMethod, ShapeField, Value, } from '../runtime.js'; import { SemanticsKind, SemanticsNode, ModuleNode, FunctionDeclareNode, VarDeclareNode, BlockNode, BasicBlockNode, IfNode, ForNode, ForInNode, ForOfNode, WhileNode, ReturnNode, } from '../semantics_nodes.js'; import { SemanticsValue, SemanticsValueKind, VarValue, LiteralValue, BinaryExprValue, FunctionCallValue, ConditionExprValue, ElementGetValue, ElementSetValue, CastValue, } from '../value.js'; import { ValueType, ValueTypeKind, ObjectType } from '../value_types.js'; import { GlobalContext, S, StatisticsObject, StatisticsInfo, } from './ir_context.js'; export class MemberDescriptionInfo { nameOffset = 0; type = 0; index = 0; } export class ObjectDescriptionInfo { name = ''; offset = 0; // the offset in data pool nameOffset = 0; // the name offset base?: ObjectDescriptionInfo; flags = 0; // int32 members: MemberDescriptionInfo[] = []; } enum ShapeMemberType { METHOD = 0, OFFSET, // 1 ACCESSOR, // 2 ACCESSOR_OFFSET, //3 } interface ShapeMemberParam { offset?: number; method?: number; getter?: number; setter?: number; } export class ShapeMemberInfo { private _value = 0; constructor(type: ShapeMemberType, param?: ShapeMemberParam) { let value = 0; if (param) { if ( type == ShapeMemberType.METHOD || type == ShapeMemberType.OFFSET ) { if (param.offset || param.method) { value = (param.offset ? param.offset! : param.method!) + 1; } } else if ( type == ShapeMemberType.ACCESSOR || type == ShapeMemberType.ACCESSOR_OFFSET ) { if (param.getter || param.setter) { const getter = param.getter ? param.getter! + 1 : 0; const setter = param.setter ? param.setter! + 1 : 0; value = (getter & 0x7fff) | ((setter & 0x7fff) << 15); } } } this._value = (value << 2) | (type & 3); } get type(): ShapeMemberType { return this._value & 3; } get offset(): number { return this._value >> 2; } get method(): number { return this._value >> 2; } get getter(): number { return (this._value >> 2) & 0x7fff; } get setter(): number { return (this._value >> 17) & 0x7fff; } get value(): number { return this._value; } } export class ShapeInfo { name = ''; offset = 0; metaOffset = 0; // meta offset members: ShapeMemberInfo[] = []; } function createObjectDescriptionName(od: ObjectDescription): string { switch (od.type) { case ObjectDescriptionType.OBJECT_LITERAL: return `${od.name}_literal`; case ObjectDescriptionType.INTERFACE: return `${od.name}_interface`; case ObjectDescriptionType.OBJECT_INSTANCE: return `${od.name}_instance`; case ObjectDescriptionType.OBJECT_CLASS: return `${od.name}_class`; } } export class RuntimeExternItem { constructor( public readonly nameOffset: number, public readonly type: number, // ExternTypeKind public readonly index: number, // index in the functions, vars, class, enums array public readonly externIndex: number, // the index in import/export module ) {} } export class RuntimeExternModule { constructor( public readonly nameOffset: number, public readonly items: RuntimeExternItem[], public readonly externIndex: number, // the index in import/export table ) {} } export class RuntimeExternModules { public offset = 0; constructor(public readonly modules: RuntimeExternModule[]) {} } enum BlockHeaderType { STRING_POOL, METAS, SHAPS, IMPORTS, EXPORTS, } class BlockHeader { count = 0; size = 0; constructor(public readonly type: BlockHeaderType) {} write(dataPool: DataPool) { dataPool.addInt32( ((this.type << 28) & 0xf0000000) | (this.count & 0x0fffffff), ); dataPool.addInt32(this.size); } update(dataPool: DataPool, start: number) { dataPool.setInt32( start, ((this.type << 28) & 0xf0000000) | (this.count & 0x0fffffff), ); dataPool.setInt32(start + 4, this.size); } } export class RuntimeData { private stringBlock = new BlockHeader(BlockHeaderType.STRING_POOL); private metaBlock = new BlockHeader(BlockHeaderType.METAS); private shapeBlock = new BlockHeader(BlockHeaderType.SHAPS); private importBlock = new BlockHeader(BlockHeaderType.IMPORTS); private exportBlock = new BlockHeader(BlockHeaderType.EXPORTS); constructor( public readonly dataPool: DataPool, public readonly globalVars: VarDeclareNode[], public readonly functions: FunctionDeclareNode[], public readonly classes: ObjectType[], public readonly metas: ObjectDescription[], public readonly shapes: Shape[], public readonly staticsInfos: Map, public readonly imports: RuntimeExternModules, public readonly exports: RuntimeExternModules, ) {} build(context: GlobalContext) { this.buildObjectDescriptions(); this.buildShapes(context); this.buildExterns(context, this.imports, this.importBlock, true); this.buildExterns(context, this.exports, this.exportBlock, false); } public objectDescriptionsMap = new Map< ObjectDescription, ObjectDescriptionInfo >(); public shapesMap = new Map(); buildObjectDescriptions() { this.metaBlock.write(this.dataPool); const start = this.dataPool.getCurrentSize(); for (const od of this.metas) { const info = this.buildObjectDescription(od); this.objectDescriptionsMap.set(od, info); } this.metaBlock.count = this.objectDescriptionsMap.size; this.metaBlock.size = this.dataPool.getCurrentSize() - start; this.metaBlock.update(this.dataPool, start - 8); } buildObjectDescription(od: ObjectDescription): ObjectDescriptionInfo { if (this.objectDescriptionsMap.has(od)) return this.objectDescriptionsMap.get(od)!; let base_info: ObjectDescriptionInfo | undefined = undefined; const base = od.base; if (base) { base_info = this.buildObjectDescription(base); } const name = createObjectDescriptionName(od); const info = new ObjectDescriptionInfo(); info.nameOffset = this.dataPool.addString(od.name); info.name = name; info.base = base_info; info.flags = od.type; Logger.debug(`=== build ObjectDescription ${name}`); // add the name of members for (const m of od.members) { const minfo = new MemberDescriptionInfo(); minfo.nameOffset = this.dataPool.addString(m.name); minfo.type = m.type; minfo.index = m.index; info.members.push(minfo); } info.offset = this.dataPool.getCurrentSize(); this.dataPool.addInt32(info.nameOffset); this.dataPool.addInt32(base_info ? base_info.offset : 0); this.dataPool.addInt32(info.flags); this.dataPool.addInt32(info.members.length); for (let i = 0; i < info.members.length; i++) { const m = info.members[i]; this.dataPool.addInt32(m.nameOffset); this.dataPool.addInt32((m.type << 16) | (m.index & 0xffff)); } return info; } buildShapes(context: GlobalContext) { this.shapeBlock.write(this.dataPool); const start = this.dataPool.getCurrentSize(); let count = 0; for (const od of this.metas) { if (od.originShape) { this.buildShape(od.originShape, 'origin', context); count++; } if (od.thisShape) { this.buildShape(od.thisShape, 'this', context); count++; } let idx = 0; od.compatibleShapes.forEach((shape, od) => { this.buildShape(shape, `comp_${idx++}`, context), count++; }); } this.shapeBlock.count = count; this.shapeBlock.size = this.dataPool.getCurrentSize() - start; this.shapeBlock.update(this.dataPool, start - 8); } buildShape(shape: Shape, suffix: string, context: GlobalContext) { const meta_info = this.objectDescriptionsMap.get(shape.meta)!; const info = new ShapeInfo(); info.name = `${meta_info.name}_${suffix}`; info.metaOffset = meta_info.offset; Logger.debug(`=== build Shape ${info.name} members: ${shape.members}`); if (shape.members) { for (const m of shape.members) { if (m) { const param: ShapeMemberParam = {}; let type: ShapeMemberType = ShapeMemberType.OFFSET; switch (m.kind) { case MemberType.FIELD: type = ShapeMemberType.OFFSET; param.offset = (m as ShapeField).offset; break; case MemberType.ACCESSOR: type = ShapeMemberType.ACCESSOR; createAccessorInfo( m as ShapeAccessor, param, context, ); break; case MemberType.METHOD: if (m.isOffset) { type = ShapeMemberType.OFFSET; param.offset = (m as ShapeMethod).methodOffset; } else { type = ShapeMemberType.METHOD; param.method = getShapeValueIndex( (m as ShapeMethod).methodValue, context, ); } break; } info.members.push(new ShapeMemberInfo(type, param)); } else { info.members.push(new ShapeMemberInfo(0)); } } } this.saveShape(info); this.shapesMap.set(shape, info); } saveShape(info: ShapeInfo) { const dataPool = this.dataPool; info.offset = dataPool.getCurrentSize(); dataPool.addInt32(info.metaOffset); dataPool.addInt32(info.members.length); for (const m of info.members) { dataPool.addInt32(m.value); } } buildExterns( context: GlobalContext, modules: RuntimeExternModules, block: BlockHeader, isImports: boolean, ) { block.write(this.dataPool); const start = this.dataPool.getCurrentSize(); modules.offset = start; for (const m of modules.modules) { this.dataPool.addInt32(m.nameOffset); this.dataPool.addInt32( m.items.length | (isImports ? 0x80000000 : 0), ); for (const i of m.items) { this.dataPool.addInt32(i.nameOffset); this.dataPool.addInt32( ((i.type & 0xf) << 28) | (i.index & 0x7fffffff), ); } } block.count = modules.modules.length; block.size = this.dataPool.getCurrentSize() - start; block.update(this.dataPool, start - 8); } } function createAccessorInfo( shape_acc: ShapeAccessor, param: ShapeMemberParam, context: GlobalContext, ) { if (shape_acc.isOffset) { param.getter = shape_acc.getterOffset; param.setter = shape_acc.setterOffset; } else { param.getter = getShapeValueIndex(shape_acc.getterValue, context); param.setter = getShapeValueIndex(shape_acc.setterValue, context); } } function getShapeValueIndex( value: Value | undefined, context: GlobalContext, ): number | undefined { if (!value || !(value instanceof VarValue)) return undefined; const var_value = value! as VarValue; if (!(var_value.ref instanceof FunctionDeclareNode)) return undefined; return context.getFunctionIndex( var_value.ref as FunctionDeclareNode, S.CALL, ); } export class IRModule { public functions: IRFunction[] = []; public dataPool = new DataPool(); public runtimeData?: RuntimeData; constructor(public module: ModuleNode) { const block = new BlockHeader(BlockHeaderType.STRING_POOL); block.write(this.dataPool); collectionStrings(module, this.dataPool); this.build(); } private build() { const context = new GlobalContext(this); const block = new BlockHeader(BlockHeaderType.STRING_POOL); block.count = this.dataPool.getStringCount(); block.size = this.dataPool.getCurrentSize(); block.update(this.dataPool, 0); // update string this.buildFunctions(context); context.finishBuild(); } private buildFunctions(context: GlobalContext) { for (const f of this.module.functions) { this.buildFunction(f, context); } } private buildFunction(f: FunctionDeclareNode, context: GlobalContext) { const bcfunc = new IRFunction(this); bcfunc.build(f, context); this.functions.push(bcfunc); } } function processValueString(value: SemanticsValue, dataPool: DataPool) { if (value.kind == SemanticsValueKind.LITERAL) { if (value.type.kind == ValueTypeKind.RAW_STRING) { dataPool.addString((value as LiteralValue).value as string); } } value.forEachChild((v) => processValueString(v, dataPool)); } function collectionStrings(module: ModuleNode, dataPool: DataPool) { module.functions.forEach((f) => { dataPool.addString(f.name); f.forEachValue((v) => processValueString(v, dataPool)); }); } ================================================ FILE: src/semantics/predefined_types.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { ValueTypeKind, ValueType, Primitive, PrimitiveType, ArrayType, SetType, MapType, FunctionType, } from './value_types.js'; import { PredefinedTypeId } from '../utils.js'; import { GetBuiltinObjectType } from './builtin.js'; import { specializeBuiltinObjectType } from './type_creator.js'; import { BuiltinNames } from '../../lib/builtin/builtin_name.js'; const predefinedTypes = new Map(); export function GetPredefinedType(typeId: number): ValueType | undefined { switch (typeId) { case PredefinedTypeId.VOID: return Primitive.Void; case PredefinedTypeId.UNDEFINED: return Primitive.Undefined; case PredefinedTypeId.NULL: return Primitive.Null; case PredefinedTypeId.NEVER: return Primitive.Never; case PredefinedTypeId.INT: return Primitive.Int; case PredefinedTypeId.NUMBER: return Primitive.Number; case PredefinedTypeId.BOOLEAN: return Primitive.Boolean; case PredefinedTypeId.RAW_STRING: return Primitive.RawString; case PredefinedTypeId.STRING: return Primitive.String; case PredefinedTypeId.ANY: return Primitive.Any; } let type = predefinedTypes.get(typeId); if (type) { return type; } type = createPredefinedType(typeId); if (type) { predefinedTypes.set(typeId, type); } return type; } function createPredefinedType(typeId: number): ValueType | undefined { switch (typeId) { case PredefinedTypeId.ARRAY: return GetBuiltinObjectType('Array'); case PredefinedTypeId.ARRAY_CONSTRUCTOR: return GetBuiltinObjectType('ArrayConstructor'); case PredefinedTypeId.ARRAY_ANY: return specializeBuiltinObjectType('Array', [Primitive.Any]); case PredefinedTypeId.ARRAY_INT: return specializeBuiltinObjectType('Array', [Primitive.Int]); case PredefinedTypeId.ARRAY_NUMBER: return specializeBuiltinObjectType('Array', [Primitive.Number]); case PredefinedTypeId.ARRAY_BOOLEAN: return specializeBuiltinObjectType('Array', [Primitive.Boolean]); case PredefinedTypeId.ARRAY_STRING: return specializeBuiltinObjectType('Array', [Primitive.String]); case PredefinedTypeId.SET_ANY: return specializeBuiltinObjectType('Set', [Primitive.Any]); case PredefinedTypeId.SET_INT: return specializeBuiltinObjectType('Set', [Primitive.Int]); case PredefinedTypeId.SET_NUMBER: return specializeBuiltinObjectType('Set', [Primitive.Number]); case PredefinedTypeId.SET_BOOLEAN: return specializeBuiltinObjectType('Set', [Primitive.Boolean]); case PredefinedTypeId.SET_STRING: return specializeBuiltinObjectType('Set', [Primitive.String]); case PredefinedTypeId.MAP_STRING_STRING: return specializeBuiltinObjectType('Map', [ Primitive.String, Primitive.String, ]); case PredefinedTypeId.MAP_STRING_ANY: return specializeBuiltinObjectType('Map', [ Primitive.String, Primitive.Any, ]); case PredefinedTypeId.MAP_INT_STRING: return specializeBuiltinObjectType('Map', [ Primitive.Int, Primitive.String, ]); case PredefinedTypeId.MAP_INT_ANY: return specializeBuiltinObjectType('Map', [ Primitive.Int, Primitive.Any, ]); case PredefinedTypeId.ERROR: return GetBuiltinObjectType('Error'); case PredefinedTypeId.ERROR_CONSTRUCTOR: return GetBuiltinObjectType('ErrorConstructor'); case PredefinedTypeId.FUNC_VOID_VOID_NONE: return new FunctionType( PredefinedTypeId.FUNC_VOID_VOID_NONE, Primitive.Void, [], undefined, undefined, 0, ); case PredefinedTypeId.FUNC_VOID_VOID_DEFAULT: return new FunctionType( PredefinedTypeId.FUNC_VOID_VOID_DEFAULT, Primitive.Void, [], ); case PredefinedTypeId.FUNC_VOID_ARRAY_ANY_DEFAULT: return new FunctionType( PredefinedTypeId.FUNC_VOID_ARRAY_ANY_DEFAULT, Primitive.Void, [GetPredefinedType(PredefinedTypeId.ARRAY_ANY)!], undefined, 0, ); case PredefinedTypeId.FUNC_ANY_ARRAY_ANY_DEFAULT: return new FunctionType( PredefinedTypeId.FUNC_ANY_ARRAY_ANY_DEFAULT, Primitive.Any, [GetPredefinedType(PredefinedTypeId.ARRAY_ANY)!], undefined, 0, ); case PredefinedTypeId.FUNC_VOID_VOID_METHOD: return new FunctionType( PredefinedTypeId.FUNC_VOID_VOID_METHOD, Primitive.Void, [], ); case PredefinedTypeId.FUNC_VOID_ARRAY_ANY_METHOD: return new FunctionType( PredefinedTypeId.FUNC_VOID_ARRAY_ANY_METHOD, Primitive.Void, [GetPredefinedType(PredefinedTypeId.ARRAY_ANY)!], undefined, 0, ); case PredefinedTypeId.FUNC_ANY_ARRAY_ANY_METHOD: return new FunctionType( PredefinedTypeId.FUNC_ANY_ARRAY_ANY_METHOD, Primitive.Any, [GetPredefinedType(PredefinedTypeId.ARRAY_ANY)!], undefined, 0, ); } return undefined; } ================================================ FILE: src/semantics/runtime.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { ValueType } from './value_types.js'; import { Type } from '../type.js'; import { InternalNames } from './internal.js'; import { DumpWriter, CreateDefaultDumpWriter } from './dump.js'; import { SemanticsValue } from './value.js'; export enum MemberType { FIELD = 1, METHOD, CONSTRUCTOR, ACCESSOR, } export interface MemberOrAccessor { method?: Value; getter?: Value; setter?: Value; } export enum MemberModifier { READONLY = 1, } export class MemberDescription { private _flags = 0; /* use modifier to represent readonly */ public modifiers = 0; private _getterType?: ValueType; private _setterType?: ValueType; constructor( public readonly name: string, public readonly type: MemberType, public readonly index: number, public readonly isOptional: boolean, public valueType: ValueType, // we will reset the value public methodOrAccessor?: MemberOrAccessor, public isOwn = true, public isStaic = false, public staticFieldInitValue: SemanticsValue | undefined = undefined, public isDeclaredCtor = true /** true iff explicitly write constructor(..) */, ) {} get isOverrid(): boolean { return (this._flags & 1) == 1; } setOverrid() { this._flags |= 1; } set offset(off: number) { this._flags = (off << 2) | (this._flags & 3); } get offset(): number { return this._flags >> 2; } set getterOffset(off: number) { this.offset = ((this.offset & 0x7fff) << 15) | (off & 0x7fff); } get getterOffset(): number { return this.offset & 0x7fff; } set setterOffset(off: number) { this.offset = (this.offset & 0x7fff) | ((off & 0x7fff) << 15); } get setterOffset(): number { return (this.offset >> 15) & 0x7fff; } set getterType(v: ValueType | undefined) { this._getterType = v; } get getterType(): ValueType | undefined { return this._getterType; } set setterType(v: ValueType | undefined) { this._setterType = v; } get setterType(): ValueType | undefined { return this._setterType; } toString(): string { let s = `${this.name} ${MemberType[this.type]} @${this.index} ${ this.isOverrid ? 'overrided' : '' } type: ${this.valueType}`; if (this.type == MemberType.ACCESSOR) { s = `${s} getter: ${this.getterOffset} setter: ${this.setterOffset}`; } else { s = `${s} offset: ${this.offset}`; } return s; } setAccessorFunction(func: Value, is_setter: boolean) { if (this.methodOrAccessor) { const obj = this.methodOrAccessor; if (is_setter) obj.setter = func; else obj.getter = func; } else { this.methodOrAccessor = is_setter ? { setter: func } : { getter: func }; } } get hasGetter(): boolean { return ( this.type == MemberType.ACCESSOR && !!this.methodOrAccessor && !!this.methodOrAccessor!.getter ); } get hasSetter(): boolean { return ( this.type == MemberType.ACCESSOR && !!this.methodOrAccessor && !!this.methodOrAccessor!.setter ); } get getter(): Value | undefined { return this.hasGetter ? this.methodOrAccessor!.getter : undefined; } get setter(): Value | undefined { return this.hasSetter ? this.methodOrAccessor!.setter : undefined; } get method(): Value | undefined { return this.type == MemberType.METHOD && this.methodOrAccessor ? this.methodOrAccessor!.method : undefined; } } export enum ObjectDescriptionType { OBJECT_LITERAL, INTERFACE, OBJECT_INSTANCE, OBJECT_CLASS, } export class ObjectDescription { private _clazz_or_instance?: ObjectDescription; // class of this instance public originShape?: Shape; public thisShape?: Shape; public compatibleShapes = new Map(); public members: MemberDescription[] = []; public fieldCount = 0; public drived = 0; private _base?: ObjectDescription; private _inited = false; private _builtin = false; public ctor?: MemberDescription; get isInited(): boolean { return this._inited; } setInited() { this._inited = true; } get isBuiltin(): boolean { return this._builtin; } setBuiltin() { this._builtin = true; } get base(): ObjectDescription | undefined { return this._base; } set base(b: ObjectDescription | undefined) { this._base = b; if (b) b.drived++; } get generic(): ObjectDescription { return this._generic ? this._generic : this; } get hasGeneric(): boolean { return !!this._generic; } constructor( public readonly name: string, public readonly type: ObjectDescriptionType, private readonly _generic?: ObjectDescription, ) {} get isLiteral(): boolean { return this.type == ObjectDescriptionType.OBJECT_LITERAL; } get isInterface(): boolean { return this.type == ObjectDescriptionType.INTERFACE; } get isObjectInstance(): boolean { return this.type == ObjectDescriptionType.OBJECT_INSTANCE; } get isObjectClass(): boolean { return this.type == ObjectDescriptionType.OBJECT_CLASS; } get instance(): ObjectDescription | undefined { return this.isObjectClass ? this._clazz_or_instance : this; } set instance(inst: ObjectDescription | undefined) { if (this.isObjectClass) this._clazz_or_instance = inst; } get clazz(): ObjectDescription | undefined { if (this.isObjectClass) return this; if (this.isObjectInstance) return this._clazz_or_instance; return undefined; } set clazz(c: ObjectDescription | undefined) { if (this.isObjectInstance) this._clazz_or_instance = c; } findMember(name: string): MemberDescription | undefined { return this.members.find((m) => m && name == m.name); } findConstructor(): MemberDescription | undefined { return this.findMember(InternalNames.CONSTRUCTOR); // return this.ctor; } updateMember( name: string, i: number, type: MemberType, is_optional = false, valueType: ValueType, is_own: boolean, methodOrAccessor?: MemberOrAccessor, is_static = false, static_field_init_value: SemanticsValue | undefined = undefined, ): MemberDescription { let super_member: MemberDescription | undefined = undefined; if (this._base && name !== 'constructor') { super_member = this._base.findMember(name); } let index = i; let slot = i; if (super_member) { index = super_member.index; // super member is override super_member.setOverrid(); slot = index; /* spread accessor flags */ if (!is_own) { methodOrAccessor = super_member.methodOrAccessor; } } else { slot = this.findFreeSlot(slot); } this.members[slot] = new MemberDescription( name, type, index, is_optional, valueType, methodOrAccessor, is_own, is_static, static_field_init_value, ); return this.members[slot]; } findFreeSlot(start: number): number { while (start < this.members.length && this.members[start]) { start++; } if (start >= this.members.length) { throw Error( `Cannot found a free slot for member of ${this.name} member start: ${start} member count: ${this.members.length}`, ); } return start; } buildShape(from: Shape): Shape { const from_meta = from.meta; if (this.compatibleShapes.has(from_meta)) return this.compatibleShapes.get(from_meta)!; if (this.hasGeneric) { const new_generice_shape = this.generic.buildShape( from.genericShape, ); const new_shape = new Shape(this, new_generice_shape); new_shape.members = new_generice_shape.members; this.compatibleShapes.set(from_meta, new_shape); return new_shape; } // else const new_shape = new Shape(this); const members = new Array(this.members.length); for (let i = 0; i < this.members.length; i++) { const to_member = this.members[i]; const to_index = to_member.index; const from_member = from_meta.findMember(to_member.name); if (from_member) { if ( from_member.type == to_member.type || ((from_member.type == MemberType.FIELD || from_member.type == MemberType.ACCESSOR) && (to_member.type == MemberType.FIELD || to_member.type == MemberType.ACCESSOR)) ) { const m = from.getMember(from_member.index); if (m) { members[to_index] = m; } } } } new_shape.members = members; this.compatibleShapes.set(from_meta, new_shape); return new_shape; } dump(writer: DumpWriter) { writer.write( `ObjectDescript: ${this.name} [${ ObjectDescriptionType[this.type] }] {`, ); writer.shift(); writer.write(`fieldCount: ${this.fieldCount}`); writer.write(`drived: ${this.drived}`); writer.write(`super: ${this._base ? this._base.name : ''}`); writer.write(`members: [`); writer.shift(); for (const m of this.members) { writer.write(m.toString()); } writer.unshift(); if (this.originShape) { if (this.originShape === this.thisShape) { writer.write(`originShape & thisShape:`); } else { writer.write(`originShape:`); } writer.shift(); this.originShape.dump(writer); writer.unshift(); } if (this.thisShape && this.thisShape !== this.originShape) { writer.write(`thisShape:`); writer.shift(); this.thisShape.dump(writer); writer.unshift(); } writer.write(`compatibleShapes: [`); writer.shift(); this.compatibleShapes.forEach((shape, od) => { writer.write(`from ${od.name}`); shape.dump(writer); }); writer.unshift(); writer.write(`]`); writer.unshift(); writer.write(`}`); } } export const UnknownObjectDescription = new ObjectDescription( 'UnknownObject', ObjectDescriptionType.INTERFACE, ); export interface Value { shape?: Shape; } export enum ShapeMemberStorage { OFFSET, // the member offset VALUE, } export class ShapeMember { constructor( public readonly kind: MemberType, public readonly storage: ShapeMemberStorage, ) {} get isOffset(): boolean { return this.storage == ShapeMemberStorage.OFFSET; } get isValue(): boolean { return this.storage == ShapeMemberStorage.VALUE; } get isEmpty(): boolean { return true; } dump(writer: DumpWriter) { writer.write(this.toString()); } } export class ShapeField extends ShapeMember { constructor(public readonly offset?: number) { super(MemberType.FIELD, ShapeMemberStorage.OFFSET); } get isEmpty(): boolean { return this.offset == undefined; } toString(): string { return `ShapeField: offset ${ this.offset != undefined ? this.offset : '' }`; } } export class ShapeAccessor extends ShapeMember { constructor( storage: ShapeMemberStorage, private _getter?: Value | number, private _setter?: Value | number, ) { super(MemberType.ACCESSOR, storage); } get getterOffset(): number | undefined { return this._getter !== undefined && this.isOffset ? (this._getter! as number) : undefined; } get getterValue(): Value | undefined { return this._getter !== undefined && this.isValue ? (this._getter! as Value) : undefined; } get getter(): Value | number | undefined { return this._getter; } get setter(): Value | number | undefined { return this._setter; } get setterOffset(): number | undefined { return this._setter !== undefined && this.isOffset ? (this._setter! as number) : undefined; } get setterValue(): Value | undefined { return this._setter !== undefined && this.isValue ? (this._setter! as Value) : undefined; } get isEmpty(): boolean { return this._getter == undefined && this._setter == undefined; } toString(): string { return `ShapeAccessor: ${this.isOffset ? 'offset' : 'value'} getter ${ this.getter }, setter: ${this.setter}`; } } export class ShapeMethod extends ShapeMember { constructor(storage: ShapeMemberStorage, private _func?: Value | number) { super(MemberType.METHOD, storage); } get methodOffset(): number | undefined { return this.isOffset && this._func !== undefined ? (this._func as number) : undefined; } get methodValue(): Value | undefined { return this.isValue && this._func !== undefined ? (this._func as Value) : undefined; } get method(): Value | number | undefined { return this._func; } get isEmpty(): boolean { return this._func == undefined; } toString(): string { return `ShapeMethod: ${this.isOffset ? 'offset' : 'value'} method ${ this.method }`; } } export class Shape { public members?: Array; constructor( public readonly meta: ObjectDescription, private readonly _generic?: Shape, ) {} get genericShape(): Shape { return this._generic ? this._generic : this; } get hasGeneric(): boolean { return !!this._generic; } getMember(idx: number): ShapeMember | undefined { if (this.members) return this.members[idx]; return undefined; } isStaticShape(): boolean { if (this.members) { for (let i = 0; i < this.members.length; i++) { if (!this.members[i]) return false; } return true; } return false; } dump(writer: DumpWriter) { writer.write(`shape: of meta ${this.meta.name} {`); writer.shift(); if (this.members) { for (const m of this.members) { if (m) m.dump(writer); else writer.write(''); } } writer.unshift(); } } //////////////////////////////////////////////// export const use_shape = true; // delete me when we removed ================================================ FILE: src/semantics/semantics_nodes.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { DumpWriter, CreateDefaultDumpWriter } from './dump.js'; import { MemberType, ObjectDescription, Shape } from './runtime.js'; import { SemanticsValue, SemanticsValueVisitor, VarValue, LiteralValue, SemanticsValueKind, } from './value.js'; import { Logger } from '../log.js'; import { Type, TSClass, TsClassField, TsClassFunc, FunctionKind, TypeKind, TSFunction, TSArray, TSTuple, } from '../type.js'; import { ValueType, ValueTypeKind, PrimitiveType, Primitive, ArrayType, SetType, MapType, UnionType, FunctionType, EnumType, ObjectType, WASM, TupleType, } from './value_types.js'; import { GetPredefinedType } from './predefined_types.js'; import { Export, Import, PredefinedTypeId, SourceLocation } from '../utils.js'; export enum SemanticsKind { EMPTY, BASIC_BLOCK, MODULE, FUNCTION, VAR_DECLARE, BLOCK, IF, RETURN, FOR, FOR_IN, FOR_OF, WHILE, DOWHILE, SWITCH, CASE_CLAUSE, DEFAULT_CLAUSE, BREAK, CONTINUE, TRY, CATCH_CLAUSE, THROW, } export interface SemanticsNodeVisitor { (node: SemanticsNode): void; } export class SemanticsNode { constructor(public kind: SemanticsKind) {} location: SourceLocation | null = null; dump(writer: DumpWriter) { writer.write(SemanticsKind[this.kind]); } forEachChild(visitor: SemanticsNodeVisitor) { return; } forEachValue(visitor: SemanticsValueVisitor) { this.forEachChild((node) => node.forEachValue(visitor)); } } export class EmptyNode extends SemanticsNode { constructor() { super(SemanticsKind.EMPTY); } } export class ReturnNode extends SemanticsNode { constructor(public expr: SemanticsValue | undefined) { super(SemanticsKind.RETURN); } dump(writer: DumpWriter) { writer.write('[RETURN]'); writer.shift(); if (this.expr) this.expr.dump(writer); writer.unshift(); } forEachValue(visitor: SemanticsValueVisitor) { if (this.expr) visitor(this.expr); super.forEachValue(visitor); } } export type VarStorageType = | SemanticsValueKind.LOCAL_VAR | SemanticsValueKind.LOCAL_CONST | SemanticsValueKind.GLOBAL_VAR | SemanticsValueKind.GLOBAL_CONST | SemanticsValueKind.PARAM_VAR | SemanticsValueKind.CLOSURE_VAR | SemanticsValueKind.CLOSURE_CONST; export enum ParameterNodeFlag { OPTIONAL, } enum VarDeclareNodeFlag { NOT_USED_IN_CLOSURE = 0, USED_IN_CLOSURE_BY_REF, USED_IN_CLOSURE_BY_VALUE, IMPORT_AS_REF = 8, } export class VarDeclareNode extends SemanticsNode { /** which context use this variable */ public curCtx?: VarDeclareNode; constructor( public storageType: VarStorageType, public type: ValueType, public name: string, public index: number, public flags: number, public initValue?: SemanticsValue, public closureIndex?: number, public belongCtx?: VarDeclareNode, public initCtx?: VarDeclareNode, public needReBinding = false, ) { super(SemanticsKind.VAR_DECLARE); this.curCtx = this.belongCtx; } copy() { const newVar = new VarDeclareNode( this.storageType, this.type, this.name, this.index, this.flags, this.initValue, this.closureIndex, this.belongCtx, this.initCtx, this.needReBinding, ); return newVar; } setUsedByClosureFunction() { if ( this.storageType == SemanticsValueKind.LOCAL_VAR || this.storageType == SemanticsValueKind.PARAM_VAR || this.storageType == SemanticsValueKind.CLOSURE_VAR ) this.flags = (this.flags & ~3) | VarDeclareNodeFlag.USED_IN_CLOSURE_BY_REF; else if ( this.storageType == SemanticsValueKind.LOCAL_CONST || this.storageType == SemanticsValueKind.CLOSURE_CONST ) this.flags = (this.flags & ~3) | VarDeclareNodeFlag.USED_IN_CLOSURE_BY_VALUE; } isUsedInClosureFunction() { return (this.flags & 3) != 0; } isUsedInClosureByRef(): boolean { return (this.flags & 3) == VarDeclareNodeFlag.USED_IN_CLOSURE_BY_REF; } isUsedInClosureByValue(): boolean { return (this.flags & 3) == VarDeclareNodeFlag.USED_IN_CLOSURE_BY_VALUE; } setImportAsRef() { this.flags |= VarDeclareNodeFlag.IMPORT_AS_REF; } isImportAsRef(): boolean { return ( (this.flags & VarDeclareNodeFlag.IMPORT_AS_REF) == VarDeclareNodeFlag.IMPORT_AS_REF ); } isRef(): boolean { return this.isUsedInClosureFunction() || this.isImportAsRef(); } toString(): string { return `Var ${this.name} ${SemanticsValueKind[this.storageType]} ${ this.type } ${this.index} ${this.flags}`; } dump(writer: DumpWriter) { writer.write(this.toString()); } } export enum FunctionOwnKind { STATIC = 1, METHOD = 2, DEFAULT = 4, DECLARE = 8, DECORATOR = 16, EXPORT = 32, START = 64, } export interface NativeSignature { paramTypes: ValueType[]; returnType: ValueType; } export class FunctionDeclareNode extends SemanticsNode { public flattenValue?: SemanticsValue; private _closureVars?: VarDeclareNode[]; public isInEnterScope = false; public importStartFuncNameList: string[] | undefined = undefined; public debugFilePath = ''; public comments: (NativeSignature | Import | Export)[] = []; constructor( public name: string, public ownKind: FunctionOwnKind, public funcType: FunctionType, public body: BlockNode, public parameters?: VarDeclareNode[], public varList?: VarDeclareNode[], public parentCtx?: VarDeclareNode /* closureContext of the parent closureEnvironment scope */, private _thisClassType?: ObjectType, ) { super(SemanticsKind.FUNCTION); } toString(): string { return `FUNCTION: ${this.name} ${FunctionOwnKind[this.ownKind]} ${ this.funcType }`; } pushClosureVarDeclare(val: VarDeclareNode) { if (!this._closureVars) this._closureVars = []; this._closureVars!.push(val); } get thisClassType(): ObjectType | undefined { return this._thisClassType; } get closureVars(): VarDeclareNode[] | undefined { return this._closureVars; } findClosureIndex(v: VarDeclareNode): number { if (!this._closureVars) return -1; for (let i = 0; i < this._closureVars!.length; i++) { const c = this._closureVars![i]; if (c === v || c.name == v.name) return i; } return -1; } findParameterIndex(v: VarDeclareNode): number { if (!this.parameters) return -1; for (let i = 0; i < this.parameters!.length; i++) { const p = this.parameters![i]; if (p === v || p.name == v.name) return i; } return -1; } dump(writer: DumpWriter) { writer.write(this.toString()); } dumpCode(writer: DumpWriter) { this.dump(writer); console.log('function parameters: '); console.log(this.parameters); this.parameters?.forEach((p) => console.log(p.type)); console.log('function vars: '); console.log(this.varList); this.varList?.forEach((v) => console.log(v.type)); writer.shift(); this.body.dump(writer); writer.unshift(); } forEachChild(visitor: SemanticsNodeVisitor) { if (this.varList) this.varList.forEach((n) => visitor(n)); if (this.parameters) this.parameters.forEach((n) => visitor(n)); visitor(this.body); } } export class BlockNode extends SemanticsNode { constructor( public statements: SemanticsNode[], public varList?: VarDeclareNode[], ) { super(SemanticsKind.BLOCK); } dump(writer: DumpWriter) { if (this.varList) { for (const v of this.varList) v.dump(writer); writer.write(''); // empty line } for (const s of this.statements) s.dump(writer); } forEachChild(visitor: SemanticsNodeVisitor) { this.statements.forEach((n) => visitor(n)); if (this.varList) this.varList.forEach((n) => visitor(n)); } } export class BasicBlockNode extends SemanticsNode { public isStartBasicBlock = false; constructor() { super(SemanticsKind.BASIC_BLOCK); } public valueNodes: SemanticsValue[] = []; pushSemanticsValue(valueNode: SemanticsValue) { this.valueNodes.push(valueNode); } dump(writer: DumpWriter) { super.dump(writer); writer.write('BASIC_BLOCK_WRITER'); writer.write(this.valueNodes.toString()); writer.write(this.valueNodes.length.toString()); writer.shift(); for (const v of this.valueNodes) v.dump(writer); writer.unshift(); } forEachValue(visitor: SemanticsValueVisitor) { this.valueNodes.forEach((v) => visitor(v)); } } export class IfNode extends SemanticsNode { constructor( public condition: SemanticsValue, public trueNode: SemanticsNode, public falseNode?: SemanticsNode, ) { super(SemanticsKind.IF); } dump(writer: DumpWriter) { writer.write('[IF]'); writer.shift(); this.condition.dump(writer); this.trueNode.dump(writer); writer.unshift(); if (this.falseNode) { writer.write('[ELSE]'); writer.shift(); this.falseNode.dump(writer); writer.unshift(); } } forEachChild(visitor: SemanticsNodeVisitor) { visitor(this.trueNode); if (this.falseNode) visitor(this.falseNode); } forEachValue(visitor: SemanticsValueVisitor) { visitor(this.condition); super.forEachValue(visitor); } } export class ForNode extends SemanticsNode { constructor( public label: string, public blockLabel: string, public continueLabel: string | null, public varList?: VarDeclareNode[], public initialize?: SemanticsNode, public condition?: SemanticsValue, public next?: SemanticsValue, public body?: SemanticsNode, ) { super(SemanticsKind.FOR); } forEachChild(visitor: SemanticsNodeVisitor) { if (this.varList) this.varList.forEach((n) => visitor(n)); if (this.initialize) visitor(this.initialize); if (this.body) visitor(this.body); } forEachValue(visitor: SemanticsValueVisitor) { if (this.condition) visitor(this.condition); super.forEachValue(visitor); } dump(writer: DumpWriter) { writer.write('[FOR]'); writer.shift(); if (this.varList) for (const v of this.varList) { v.dump(writer); } if (this.initialize) this.initialize.dump(writer); if (this.condition) this.condition.dump(writer); if (this.next) this.next.dump(writer); if (this.body) this.body.dump(writer); writer.unshift(); } } export class ForInNode extends SemanticsNode { constructor( public key: VarDeclareNode, public target: VarValue, public body?: SemanticsNode, ) { super(SemanticsKind.FOR_IN); } forEachChild(visitor: SemanticsNodeVisitor) { visitor(this.key); if (this.body) visitor(this.body); } forEachValue(visitor: SemanticsValueVisitor) { visitor(this.target); super.forEachValue(visitor); } } export class ForOfNode extends SemanticsNode { constructor( public value: VarDeclareNode, public target: VarValue, public body?: SemanticsNode, ) { super(SemanticsKind.FOR_OF); } forEachChild(visitor: SemanticsNodeVisitor) { if (this.body) visitor(this.body); } forEachValue(visitor: SemanticsValueVisitor) { visitor(this.target); super.forEachValue(visitor); } } export type ForInOrOfNode = ForInNode | ForOfNode; export class WhileNode extends SemanticsNode { constructor( kind: SemanticsKind.WHILE | SemanticsKind.DOWHILE, public label: string, public blockLabel: string, public continueLabel: string | null, public condition: SemanticsValue, public body?: SemanticsNode, ) { super(kind); } isDoWhile(): boolean { return this.kind == SemanticsKind.DOWHILE; } dump(writer: DumpWriter) { if (this.isDoWhile()) writer.write('[DO-WHILE]'); else writer.write('[WHILE]'); writer.shift(); this.condition.dump(writer); if (this.body) this.body.dump(writer); writer.unshift(); } forEachChild(visitor: SemanticsNodeVisitor) { if (this.body) visitor(this.body); } forEachValue(visitor: SemanticsValueVisitor) { visitor(this.condition); super.forEachValue(visitor); } } export class SwitchNode extends SemanticsNode { constructor( public label: string, public breakLabel: string, public condition: SemanticsValue, public caseClause: CaseClauseNode[], public defaultClause?: DefaultClauseNode, ) { super(SemanticsKind.SWITCH); } dump(writer: DumpWriter) { writer.write('[SWITCH]'); writer.shift(); for (const c of this.caseClause) c.dump(writer); if (this.defaultClause) this.defaultClause.dump(writer); writer.unshift(); } forEachChild(visitor: SemanticsNodeVisitor) { this.caseClause.forEach((n) => visitor(n)); if (this.defaultClause) visitor(this.defaultClause); } forEachValue(visitor: SemanticsValueVisitor) { visitor(this.condition); super.forEachValue(visitor); } } export class CaseClauseNode extends SemanticsNode { constructor(public caseVar: SemanticsValue, public body?: SemanticsNode) { super(SemanticsKind.CASE_CLAUSE); } dump(writer: DumpWriter) { writer.write('[CASE]'); this.caseVar.dump(writer); if (this.body) this.body.dump(writer); } forEachChild(visitor: SemanticsNodeVisitor) { if (this.body) visitor(this.body); } forEachValue(visitor: SemanticsValueVisitor) { visitor(this.caseVar); super.forEachValue(visitor); } } export class DefaultClauseNode extends SemanticsNode { constructor(public body?: SemanticsNode) { super(SemanticsKind.DEFAULT_CLAUSE); } dump(writer: DumpWriter) { writer.write('[DEFAULT]'); if (this.body) this.body.dump(writer); } forEachChild(visitor: SemanticsNodeVisitor) { if (this.body) visitor(this.body); } } export class BreakNode extends SemanticsNode { constructor(public label: string) { super(SemanticsKind.BREAK); } } export class ContinueNode extends SemanticsNode { constructor(public label: string) { super(SemanticsKind.CONTINUE); } } export class ThrowNode extends SemanticsNode { constructor(public throwExpr: SemanticsValue) { super(SemanticsKind.THROW); } dump(writer: DumpWriter) { writer.write('[THROW]'); this.throwExpr.dump(writer); } } export class CatchClauseNode extends SemanticsNode { public catchVar: SemanticsValue | undefined = undefined; constructor(public body: SemanticsNode) { super(SemanticsKind.CATCH_CLAUSE); } dump(writer: DumpWriter) { writer.write('[CATCH]'); if (this.catchVar) { this.catchVar.dump(writer); } this.body.dump(writer); } forEachChild(visitor: SemanticsNodeVisitor) { visitor(this.body); } } export class TryNode extends SemanticsNode { constructor( public label: string, public body: SemanticsNode, public catchClause?: CatchClauseNode, public finallyBlock?: SemanticsNode, ) { super(SemanticsKind.TRY); } dump(writer: DumpWriter) { writer.write('[TRY]'); this.body.dump(writer); if (this.catchClause) { this.catchClause.dump(writer); } if (this.finallyBlock) { writer.write('[FINALLY]'); this.finallyBlock.dump(writer); } } forEachChild(visitor: SemanticsNodeVisitor) { visitor(this.body); if (this.catchClause) visitor(this.catchClause); if (this.finallyBlock) visitor(this.finallyBlock); } } export type ExternType = | FunctionDeclareNode | VarDeclareNode | EnumType | ObjectType; export enum ExternTypeKind { FUNCTION, CLASS, VAR, ENUM, } export interface ExternItem { readonly kind: ExternTypeKind; readonly name: string; type: ExternType; } export class ExternModule { public readonly items = new Set(); constructor( public readonly name: string, public readonly isImport = true, ) {} addItem(kind: ExternTypeKind, name: string, type: ExternType) { this.items.add({ kind, name, type }); } } export class ExternModuleManager { public readonly modules = new Set(); constructor(public readonly isImport = true) {} add(m: ExternModule) { this.modules.add(m); } } export class ModuleNode extends SemanticsNode { public types: Map = new Map(); public typeByIds: Map = new Map(); public enums: Map = new Map(); public globalVars: VarDeclareNode[] = []; public functions = new Set(); public globalInitFunc: FunctionDeclareNode | undefined = undefined; public globalValues: VarValue[] = []; public objectDescriptions: ObjectDescription[] = []; public namedTypes = new Map(); // save the named object type public recObjectTypeGroup = new Array(); public readonly exports = new ExternModuleManager(false); public readonly imports = new ExternModuleManager(true); constructor() { super(SemanticsKind.MODULE); } dumpGlobalVars(writer: DumpWriter) { for (const v of this.globalVars) v.dump(writer); } dumpFunctions(writer: DumpWriter) { for (const f of this.functions) f.dump(writer); } dumpTypes(writer: DumpWriter) { writer.write('types: {'); writer.shift(); this.types.forEach((v, _) => { writer.write(v.toString()); }); writer.unshift(); writer.write('}'); } dumpEnums(writer: DumpWriter) { writer.write('enums: {'); writer.shift(); this.enums.forEach((v, k) => writer.write(v.toString())); writer.unshift(); writer.write('}'); } dump(writer: DumpWriter) { writer.write('ModuleNode ['); writer.shift(); this.dumpTypes(writer); this.dumpGlobalVars(writer); this.dumpFunctions(writer); this.dumpEnums(writer); this.dumpObjectDescriptions(writer); writer.unshift(); writer.write(']'); writer.unshift(); writer.write(']'); } dumpObjectDescriptions(writer: DumpWriter) { writer.write(`====================== object descripts ===========`); for (const od of this.objectDescriptions) od.dump(writer); writer.write(`===================================================`); } dumpCodeTrees(writer: DumpWriter) { for (const f of this.functions) { f.dumpCode(writer); writer.write(''); } } findValueTypeByType(type: Type): ValueType | undefined { if (type == undefined || type == null) return Primitive.Any; switch (type.kind) { case TypeKind.VOID: return Primitive.Void; case TypeKind.BOOLEAN: return Primitive.Boolean; case TypeKind.ANY: return Primitive.Any; case TypeKind.NUMBER: return Primitive.Number; case TypeKind.STRING: return Primitive.String; case TypeKind.GENERIC: return Primitive.Generic; case TypeKind.NULL: return Primitive.Null; case TypeKind.UNDEFINED: return Primitive.Undefined; case TypeKind.WASM_I32: return WASM.I32; case TypeKind.WASM_I64: return WASM.I64; case TypeKind.WASM_F32: return WASM.F32; case TypeKind.WASM_F64: return WASM.F64; case TypeKind.WASM_ANYREF: return WASM.ANYREF; } const valueType = this.types.get(type); if (valueType) return valueType; switch (type.kind) { case TypeKind.ARRAY: { const elementType = this.findValueTypeByType( (type as TSArray).elementType, ); Logger.debug( `===== element: ${ (type as TSArray).elementType.kind } elementType: ${elementType}`, ); if (!elementType) return undefined; switch (elementType.kind) { case ValueTypeKind.ANY: return GetPredefinedType(PredefinedTypeId.ARRAY_ANY)!; case ValueTypeKind.INT: return GetPredefinedType(PredefinedTypeId.ARRAY_INT)!; case ValueTypeKind.NUMBER: return GetPredefinedType( PredefinedTypeId.ARRAY_NUMBER, )!; case ValueTypeKind.STRING: return GetPredefinedType( PredefinedTypeId.ARRAY_STRING, )!; } for (const t of this.types.values()) { Logger.debug(`==== t: ${t}, elementType: ${elementType}`); if ( t.kind == ValueTypeKind.ARRAY && (t as ArrayType).element.equals(elementType) ) { return t; } } break; } //case TypeKind.MAP case TypeKind.FUNCTION: { const ts_func = type as TSFunction; const retType = this.findValueTypeByType(ts_func.returnType); if (!retType) return undefined; const params: ValueType[] = []; for (const p of ts_func.getParamTypes()) { const vt = this.findValueTypeByType(p); if (!vt) return undefined; params.push(vt); } if ( retType.kind == ValueTypeKind.VOID && (params.length == 0 || (params.length == 1 && params[0].kind == ValueTypeKind.VOID)) ) { return GetPredefinedType( PredefinedTypeId.FUNC_VOID_VOID_METHOD, ); } for (const t of this.types.values()) { if (t.kind == ValueTypeKind.FUNCTION) { const f = t as FunctionType; if (!f.returnType.equals(retType)) continue; if (params.length != f.argumentsType.length) continue; if (f.restParamIdx != ts_func.restParamIdx) continue; let i = 0; for (i = 0; i < params.length; i++) { if (!params[i].equals(f.argumentsType[i])) break; } if (i == params.length) { return t; // found } } } break; } case TypeKind.TUPLE: { let isEqual = false; for (const t of this.types.values()) { Logger.debug(`==== t: ${t}, TupleType: ${type}`); if ( t.kind == ValueTypeKind.TUPLE && (t as TupleType).elements.length === (type as TSTuple).elements.length ) { for ( let i = 0; i < (type as TSTuple).elements.length; i++ ) { const elemType = this.findValueTypeByType( (type as TSTuple).elements[i], ); if ( !elemType || (t as TupleType).elements[i].typeId !== elemType.typeId ) { isEqual = false; break; } else { isEqual = true; } } if (isEqual) { return t; } } } break; } } return valueType; } findArrayValueType(elementType: ValueType): ValueType | undefined { switch (elementType.kind) { case ValueTypeKind.NUMBER: return GetPredefinedType(PredefinedTypeId.ARRAY_NUMBER); case ValueTypeKind.STRING: return GetPredefinedType(PredefinedTypeId.ARRAY_STRING); case ValueTypeKind.ANY: return GetPredefinedType(PredefinedTypeId.ARRAY_ANY); } if (!elementType) return undefined; for (const t of this.types.values()) { Logger.debug(`==== t: ${t}, elementType: ${elementType}`); if ( t.kind == ValueTypeKind.ARRAY && (t as ArrayType).element.equals(elementType) ) { return t; } } return undefined; } findTupleElementTypes(elementTypes: ValueType[]): ValueType | undefined { for (const t of this.types.values()) { if ( t.kind == ValueTypeKind.TUPLE && (t as TupleType).elements.toString() === elementTypes.toString() ) { Logger.debug( `==== t: ${t}, elementTypes in tupleType: ${elementTypes}`, ); return t; } } return undefined; } findObjectValueType(tsclass: TSClass): ValueType | undefined { for (const t of this.types.keys()) { if (!(t instanceof TSClass)) { continue; } if (t === tsclass) { return this.types.get(t); } } return undefined; } } ================================================ FILE: src/semantics/statement_builder.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import ts from 'typescript'; import { Logger } from '../log.js'; import { buildExpression, newCastValue, newBinaryExprValue, buildFunctionExpression, } from './expression_builder.js'; import { generateChildrenFunctionScope } from './index.js'; import { SemanticsKind, SemanticsNode, VarDeclareNode, VarStorageType, EmptyNode, BasicBlockNode, BlockNode, IfNode, ForNode, WhileNode, CaseClauseNode, DefaultClauseNode, SwitchNode, ReturnNode, BreakNode, ContinueNode, CatchClauseNode, TryNode, ThrowNode, } from './semantics_nodes.js'; import { Variable } from '../variable.js'; import { Scope, ScopeKind } from '../scope.js'; import { Statement, IfStatement, BlockStatement, ReturnStatement, BaseLoopStatement, ForStatement, ExpressionStatement, CaseClause, DefaultClause, CaseBlock, SwitchStatement, BreakStatement, VariableStatement, FunctionDeclarationStatement, CatchClauseStatement, TryStatement, ThrowStatement, ContinueStatement, } from '../statement.js'; import { ReBindingValue, SemanticsValue, SemanticsValueKind, VarValue, VarValueKind, } from './value.js'; import { ValueType, ValueTypeKind, Primitive } from './value_types.js'; import { BuildContext, SymbolValue, SymbolKey, SymbolKeyToString, ValueReferenceKind, } from './builder_context.js'; import { createType } from './type_creator.js'; import { getNodeLoc } from '../utils.js'; import { getConfig } from '../../config/config_mgr.js'; export function createFromVariable( v: Variable, as_global: boolean, context: BuildContext, ): VarDeclareNode { let storageType: VarStorageType = as_global ? SemanticsValueKind.GLOBAL_CONST : SemanticsValueKind.LOCAL_CONST; if (!v.isConst() && !v.isReadOnly()) { if (as_global) storageType = SemanticsValueKind.GLOBAL_VAR; else storageType = SemanticsValueKind.LOCAL_VAR; } const type = createType(context, v.varType) as ValueType; if (!type) { throw Error( `Cannot found the type of ${v.varType} for variable:"${v.varName}(${ v.mangledName })" owner scope: ${v.scope ? v.scope!.mangledName : 'No Scope'}`, ); } Logger.debug( `=== find variable "${v.varName}(${v.mangledName} in scope ${ v.scope ? v.scope!.mangledName : '' })" type: ${type}`, ); return new VarDeclareNode( storageType, type!, v.varName, v.varIndex, 0, undefined, v.closureIndex, v.belongCtx ? createFromVariable(v.belongCtx, false, context) : undefined, v.initContext ? createFromVariable(v.initContext, false, context) : undefined, v.needReBinding, ); } export function createLocalSymbols( scope: Scope, context: BuildContext, ): [VarDeclareNode[] | undefined, Map | undefined] { let varList: VarDeclareNode[] | undefined = undefined; let symbols: Map | undefined = undefined; const vararr = scope!.varArray; if (vararr.length > 0) { symbols = new Map(); varList = []; for (const v of vararr) { const node = createFromVariable(v, false, context); const value = new VarValue( node.storageType, node.type, node, node.index, ); Logger.debug(`=== var ${v.varName} value: ${value}`); Logger.debug( `=== createLocalSymbols ${SymbolKeyToString( v, )} value: ${value.toString()}`, ); varList.push(node); symbols.set(v, value); } } for (const child of scope!.children) { Logger.debug( `====== createLocalSymbols child scope:${ child.kind }, ${child.getName()}`, ); if (child.kind == ScopeKind.NamespaceScope) { if (symbols == undefined) symbols = new Map(); const value = new VarValue( SemanticsValueKind.GLOBAL_CONST, Primitive.Namespace, child, child.getName(), ); symbols.set(child, value); } } return [varList, symbols]; } function buildBlock( block: BlockStatement, context: BuildContext, ): SemanticsNode { const scope = block.getScope(); const [statements, varList] = buildStatementsWithScope( scope!, scope!.statements, context, ); return new BlockNode(statements, varList); } function buildStatementsWithScope( scope: Scope, statementsFrom: Statement[], context: BuildContext, ): [SemanticsNode[], VarDeclareNode[] | undefined] { const statements: SemanticsNode[] = []; let basic_block: BasicBlockNode | undefined = undefined; context.push(scope); const [varList, symbols] = createLocalSymbols(scope, context); if (symbols) { symbols!.forEach((v, k) => Logger.debug( `== block local ${SymbolKeyToString(k)}, ${v.toString()}`, ), ); context.updateNamedSymbolByScope(scope, symbols); } // build the child function scope generateChildrenFunctionScope(context, scope); for (const st of statementsFrom) { const r = buildStatement(st, context); if (r instanceof SemanticsValue) { if (!basic_block) { basic_block = new BasicBlockNode(); statements.push(basic_block); } basic_block.pushSemanticsValue(r as SemanticsValue); } else { basic_block = undefined; const node = r as SemanticsNode; statements.push(node); } } context.pop(); return [statements, varList]; } function buildCaseClauseStatements( clause: CaseClause | DefaultClause, context: BuildContext, ): SemanticsNode { const scope = clause.getScope(); const [statements, varList] = buildStatementsWithScope( scope!, clause.caseStatements, context, ); return new BlockNode(statements, varList); } function buildVariableStatement( statement: VariableStatement, context: BuildContext, ): SemanticsNode { const basic_block = new BasicBlockNode(); for (const v of statement.varArray) { if (v.initExpression != null) { const init_value = buildExpression(v.initExpression, context); const ret = context.findSymbol(v.varName); if (!ret) { throw Error(`var ${v.varName} is not decleared`); } let var_value: VarValue | undefined = undefined; if (ret instanceof VarDeclareNode) { const var_decl = ret as VarDeclareNode; var_decl.initValue = init_value; var_value = new VarValue( var_decl.storageType as VarValueKind, var_decl.type, var_decl, var_decl.index, ); } else if (ret instanceof VarValue) { var_value = ret as VarValue; if (var_value.ref instanceof VarDeclareNode) { var_value.ref.initValue = init_value; } } else { throw Error(`var ${v.varName} unexcept type ${ret}`); } context.pushReference(ValueReferenceKind.RIGHT); context.popReference(); const assignment = newBinaryExprValue( var_value!.type, ts.SyntaxKind.EqualsToken, var_value!, init_value, ); if (v.tsNode && getConfig().sourceMap) { assignment.location = getNodeLoc(v.tsNode); } basic_block.pushSemanticsValue(assignment); } } return basic_block; } function buildStatementAsNode( statement: Statement, context: BuildContext, ): SemanticsNode { const r = buildStatement(statement, context); if (r instanceof SemanticsValue) { const b = new BasicBlockNode(); b.pushSemanticsValue(r as SemanticsValue); return b; } return r as SemanticsNode; } function buildIfStatement( statement: IfStatement, context: BuildContext, ): SemanticsNode { context.pushReference(ValueReferenceKind.RIGHT); let condition = buildExpression(statement.ifCondition, context); context.popReference(); condition = newCastValue(Primitive.Boolean, condition); const trueStmt = buildStatementAsNode(statement.ifIfTrue, context); let falseStmt: SemanticsNode | undefined = undefined; if (statement.ifIfFalse != null) falseStmt = buildStatementAsNode(statement.ifIfFalse, context); return new IfNode(condition, trueStmt, falseStmt); } function buildReturnStatement( statement: ReturnStatement, context: BuildContext, ): SemanticsNode { context.pushReference(ValueReferenceKind.RIGHT); let returnvalue = statement.returnExpression != null ? buildExpression(statement.returnExpression!, context) : undefined; context.popReference(); if (returnvalue) { const func = context.currentFunction(); if (func) { const func_type = func.funcType; const ret_type = func_type.returnType; if (ret_type.kind !== ValueTypeKind.VOID) { if (!ret_type.equals(returnvalue.type)) { returnvalue = newCastValue(ret_type, returnvalue); } } } } return new ReturnNode(returnvalue); } function buildBaseLoopStatement( statement: BaseLoopStatement, context: BuildContext, ): SemanticsNode { context.pushReference(ValueReferenceKind.RIGHT); let condition = buildExpression(statement.loopCondtion, context); context.popReference(); condition = newCastValue(Primitive.Boolean, condition); const body = buildStatementAsNode(statement.loopBody, context); return new WhileNode( statement.statementKind == ts.SyntaxKind.WhileStatement ? SemanticsKind.WHILE : SemanticsKind.DOWHILE, statement.loopLabel, statement.loopBlockLabel, statement.loopContinueLable, condition, body, ); } function buildForStatement( statement: ForStatement, context: BuildContext, ): SemanticsNode { // TODO process var Logger.debug( `==== statement: scope: ${SymbolKeyToString(statement.getScope()!)}`, ); const scope = statement.getScope()!; const [varList, symbols] = createLocalSymbols(scope, context); context.push(scope, symbols); const initialize = statement.forLoopInitializer != null ? buildStatementAsNode(statement.forLoopInitializer, context) : undefined; context.pushReference(ValueReferenceKind.RIGHT); const condition = statement.forLoopCondtion != null ? buildExpression(statement.forLoopCondtion, context) : undefined; const next = statement.forLoopIncrementor != null ? buildExpression(statement.forLoopIncrementor, context) : undefined; context.popReference(); const body = buildStatementAsNode(statement.forLoopBody, context); const reBindedStmts = [body]; if (varList) { const b = new BasicBlockNode(); const handledCtxs: VarDeclareNode[] = []; for (const varNode of varList) { if (varNode.needReBinding && varNode.belongCtx) { if (!handledCtxs.includes(varNode.belongCtx)) { handledCtxs.push(varNode.belongCtx); const reBindingValue = new ReBindingValue( varNode.belongCtx, ); b.pushSemanticsValue(reBindingValue); } } } reBindedStmts.push(b); } const finally_body = new BlockNode(reBindedStmts); context.pop(); /*TODO: varList can be removed from ForNode, since varList is recorded in outter block scope */ return new ForNode( statement.forLoopLabel, statement.forLoopBlockLabel, statement.forContinueLable, varList, initialize, condition, next, finally_body, ); } function buildSwitchStatement( statement: SwitchStatement, context: BuildContext, ): SemanticsNode { context.pushReference(ValueReferenceKind.RIGHT); const condition = buildExpression(statement.switchCondition, context); context.popReference(); const case_block = statement.switchCaseBlock as CaseBlock; const case_nodes: CaseClauseNode[] = []; let default_node: DefaultClauseNode | undefined = undefined; for (const clause of case_block.caseCauses) { if (clause.statementKind == ts.SyntaxKind.DefaultClause) { const default_cause = buildCaseClauseStatements( clause as DefaultClause, context, ); if (getConfig().sourceMap) { default_cause.location = getNodeLoc(clause.tsNode!); } default_node = new DefaultClauseNode(default_cause); } else { const case_clause = clause as CaseClause; context.pushReference(ValueReferenceKind.RIGHT); const case_expr = buildExpression(case_clause.caseExpr, context); context.popReference(); const cause = buildCaseClauseStatements(case_clause, context); if (getConfig().sourceMap) { cause.location = getNodeLoc(case_clause.tsNode!); } case_nodes.push(new CaseClauseNode(case_expr, cause)); } } return new SwitchNode( case_block.switchLabel, case_block.breakLabel, condition, case_nodes, default_node, ); } function buildBreakStatement(statement: BreakStatement): SemanticsNode { return new BreakNode(statement.breakLabel); } function buildContinueStatement(statement: ContinueStatement): SemanticsNode { return new ContinueNode(statement.continueLabel); } function buildThrowStatement( statement: ThrowStatement, context: BuildContext, ): SemanticsNode { const throw_expr = buildExpression(statement.expr, context); return new ThrowNode(throw_expr); } function buildCatchClauseStatement( statement: CatchClauseStatement, context: BuildContext, ): SemanticsNode { const catch_block_node = buildBlock(statement.catchBlockStmt, context); const catch_clause_node = new CatchClauseNode(catch_block_node); if (statement.catchVar) { const catch_var_value = buildExpression(statement.catchVar, context); catch_clause_node.catchVar = catch_var_value; } return catch_clause_node; } function buildTryStatement( statement: TryStatement, context: BuildContext, ): SemanticsNode { const try_block_node = buildBlock(statement.tryBlockStmt, context); if (getConfig().sourceMap) { try_block_node.location = getNodeLoc(statement.tryBlockStmt.tsNode!); } let catch_clause_node = undefined; if (statement.catchClauseStmt) { catch_clause_node = buildCatchClauseStatement( statement.catchClauseStmt, context, ); if (getConfig().sourceMap) { try_block_node.location = getNodeLoc( statement.catchClauseStmt.tsNode!, ); } } let finally_block_node = undefined; if (statement.finallyBlockStmt) { finally_block_node = buildBlock(statement.finallyBlockStmt, context); if (getConfig().sourceMap) { try_block_node.location = getNodeLoc( statement.finallyBlockStmt.tsNode!, ); } } return new TryNode( statement.label, try_block_node, catch_clause_node as CatchClauseNode, finally_block_node, ); } export function buildStatement( statement: Statement, context: BuildContext, ): SemanticsNode | SemanticsValue { Logger.debug( `======= buildStatement: ${ts.SyntaxKind[statement.statementKind]}`, ); let res: SemanticsNode | SemanticsValue | null = null; try { switch (statement.statementKind) { case ts.SyntaxKind.Block: res = buildBlock(statement as BlockStatement, context); break; case ts.SyntaxKind.IfStatement: res = buildIfStatement(statement as IfStatement, context); break; case ts.SyntaxKind.ReturnStatement: res = buildReturnStatement( statement as ReturnStatement, context, ); break; case ts.SyntaxKind.DoStatement: /* falls through */ case ts.SyntaxKind.WhileStatement: res = buildBaseLoopStatement( statement as BaseLoopStatement, context, ); break; case ts.SyntaxKind.ForStatement: res = buildForStatement(statement as ForStatement, context); break; /* falls through */ case ts.SyntaxKind.ExpressionStatement: res = buildExpression( (statement as ExpressionStatement).expression, context, ); break; /* falls through */ case ts.SyntaxKind.CaseClause: //return buildCaseClauseStatement(statement as CaseClause, context); break; case ts.SyntaxKind.DefaultClause: //return buildDefaultClauseStatement(statement as DefaultClause, context); break; // call it in buildCaseClauseStatements case ts.SyntaxKind.SwitchStatement: res = buildSwitchStatement( statement as SwitchStatement, context, ); break; /* falls through */ case ts.SyntaxKind.CaseBlock: break; // call it in buildSwitchStatement statement case ts.SyntaxKind.BreakStatement: res = buildBreakStatement(statement as BreakStatement); break; case ts.SyntaxKind.ContinueStatement: res = buildContinueStatement(statement as ContinueStatement); break; case ts.SyntaxKind.VariableStatement: return buildVariableStatement( statement as VariableStatement, context, ); case ts.SyntaxKind.FunctionDeclaration: { if (!context.currentFunction()) return new EmptyNode(); // global statement const funcStmt = statement as FunctionDeclarationStatement; const funcScope = funcStmt.funcScope; const var_value_tmp = context.findSymbolKey(funcStmt.tmpVar!); if (!var_value_tmp) { throw Error( `Cannot found the function scope var ${funcScope.getName()}`, ); } const var_value = var_value_tmp! as VarValue; const new_closure = buildFunctionExpression(funcScope, context); return newBinaryExprValue( var_value.type, ts.SyntaxKind.EqualsToken, var_value, new_closure, ); } case ts.SyntaxKind.ThrowStatement: res = buildThrowStatement(statement as ThrowStatement, context); break; case ts.SyntaxKind.TryStatement: res = buildTryStatement(statement as TryStatement, context); break; case ts.SyntaxKind.EmptyStatement: res = new EmptyNode(); break; /* falls through */ } if (res && getConfig().sourceMap) { res.location = statement.debugLoc; } if (!res) { res = new EmptyNode(); } return res; } catch (e: any) { console.log(e); Logger.error(e); const tsNode = statement.tsNode!; const sourceFile = tsNode.getSourceFile(); const start = tsNode.getStart(sourceFile); const startLineInfo = sourceFile.getLineAndCharacterOfPosition(start); Logger.error( `[ERROR] @ "${sourceFile.fileName}" line: ${ startLineInfo.line + 1 } @${ startLineInfo.character } end: ${tsNode.getEnd()} width: ${tsNode.getWidth(sourceFile)}`, ); Logger.error(`Source: ${tsNode.getFullText(sourceFile)}`); throw Error(e); } } ================================================ FILE: src/semantics/type_creator.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { Type, TSClass, FunctionKind, TypeKind, TSFunction, TSArray, TSUnion, TSTypeParameter, TSEnum, TSContext, TSTypeWithArguments, WasmArrayType, TSTuple, WasmStructType, } from '../type.js'; import { InternalNames } from './internal.js'; import { IsBuiltInType, IsBuiltInTypeButAny, IsBuiltInObjectType, GetAndRemoveObjectSpecializeList, AddSpecializeObjectType, GetBuiltinObjectType, IsBuiltinObject, SpecializeInfo, } from './builtin.js'; import { Logger } from '../log.js'; import { ValueType, ValueTypeKind, Primitive, ArrayType, UnionType, FunctionType, TypeParameterType, EnumType, ObjectType, ObjectTypeFlag, ClosureContextType, ValueTypeWithArguments, WASMArrayType, TupleType, WASMStructType, } from './value_types.js'; import { BuildContext } from './builder_context.js'; import { SemanticsValue, VarValue, VarValueKind } from './value.js'; import { FunctionDeclareNode } from './semantics_nodes.js'; import { MemberType, ObjectDescription, ObjectDescriptionType, MemberDescription, MemberOrAccessor, Shape, ShapeMember, ShapeField, ShapeMethod, ShapeAccessor, Value, ShapeMemberStorage, } from './runtime.js'; import { buildExpression, newCastValue } from './expression_builder.js'; import { DefaultTypeId } from '../utils.js'; import { BuiltinNames } from '../../lib/builtin/builtin_name.js'; export function isObjectType(kind: ValueTypeKind): boolean { return ( kind == ValueTypeKind.OBJECT || kind == ValueTypeKind.ARRAY || kind == ValueTypeKind.SET || kind == ValueTypeKind.MAP || kind == ValueTypeKind.FUNCTION || kind == ValueTypeKind.INTERSECTION ); } export function isNullValueType(kind: ValueTypeKind): boolean { return ( kind == ValueTypeKind.NULL || kind == ValueTypeKind.UNDEFINED || kind == ValueTypeKind.NEVER ); } export function createArrayType( context: BuildContext, element_type: ValueType, arr_type?: Type, ): ArrayType { const array_type = context.module.findArrayValueType(element_type); if (array_type) return array_type as ArrayType; return specializeBuiltinObjectType('Array', [element_type])! as ArrayType; } export function createTupleType( context: BuildContext, element_types: ValueType[], ): TupleType { const tuple_type = context.module.findTupleElementTypes(element_types); if (tuple_type) { return tuple_type as TupleType; } return new TupleType(context.nextTypeId(), element_types); } function createTypeScores(): Map { const m = new Map(); // the integer part mean the 'type class' // the decimal part mean the 'type level' // the types with same 'class' can cast each other // the same 'class' types have diffrent 'level' // the range of higher 'level' type, include the range of low 'level' type; m.set(ValueTypeKind.NUMBER, 1.6); m.set(ValueTypeKind.INT, 1.5); m.set(ValueTypeKind.BOOLEAN, 1.4); m.set(ValueTypeKind.STRING, 2.5); m.set(ValueTypeKind.RAW_STRING, 2.4); return m; } const type_scores = createTypeScores(); function collectWideTypes(types: Set): ValueType[] { const objectTypes: ValueType[] = []; let wideType: ValueType | undefined = undefined; for (const ty of types) { let t = ty; while (t) { if (t.kind == ValueTypeKind.UNION) { t = (t as UnionType).wideType; continue; } if (t.kind == ValueTypeKind.TYPE_PARAMETER) { t = (t as TypeParameterType).wideType; continue; } if (t.kind == ValueTypeKind.ENUM) { t = (t as EnumType).memberType; continue; } break; } if (t.kind == ValueTypeKind.NAMESPACE) { wideType = Primitive.Any; break; } else if (isObjectType(t.kind)) { objectTypes.push(t); } if (wideType == undefined) { wideType = t; continue; } if (wideType!.equals(t)) { continue; } if (wideType!.kind == ValueTypeKind.FUNCTION) { wideType = Primitive.Any; // TODO Fixed me, try find the same functions break; } if (isObjectType(t.kind) && isObjectType(wideType!.kind)) { continue; } if ( t.kind == ValueTypeKind.VOID || t.kind == ValueTypeKind.NULL || t.kind == ValueTypeKind.UNDEFINED || t.kind == ValueTypeKind.NEVER || t.kind == ValueTypeKind.GENERIC // TODO maybe any type ) { continue; } const t_score = type_scores.get(t.kind); const wide_score = type_scores.get(wideType!.kind); if (t_score && wide_score) { /* eg. t.kind is ValueTypeKind.NUMBER, wideType.kind is ValueTypeKind.INT so t_score is 1.6, wide_scope is 1.5 because Math.floor(t_score ) == Math.floor(wide_scope) is true the `int` type and `number` type are same 'class', so, `int` type can cast to `number` type and, t_score > wide_score, wideType should be `number` type, because `number` has the hiegher 'level' */ // if the integer part of scores are equal, the types are same 'class' if (Math.floor(t_score) == Math.floor(wide_score)) { if (t_score > wide_score) { wideType = t; // use the higher 'level' type as wideType } continue; } } wideType = Primitive.Any; break; } if (!wideType) return [Primitive.Any]; if (wideType.kind == ValueTypeKind.ANY) return [Primitive.Any]; if (objectTypes.length == 0) { return [wideType]; } return [Primitive.Any]; } function createUnionInterfaceType( typeId: number, obj_types: ValueType[], context: BuildContext, ): ObjectType { const members = new Map(); const obj_type = obj_types[0] as ObjectType; const meta = obj_type.isClassObject() ? obj_type.meta.instance! : obj_type.meta; for (const m of meta.members) { members.set(m.name, m); } for (let i = 1; i < obj_types.length; i++) { const obj_type = obj_types[i] as ObjectType; const meta = obj_type.isClassObject() ? obj_type.meta.instance! : obj_type.meta; for (const m of meta.members) { if (!members.has(m.name) || members.get(m.name)!.type != m.type) { members.delete(m.name); // delete the uncommon keys } } } const inst_members: MemberDescription[] = []; members.forEach((v, k) => { const m = new MemberDescription( v.name, v.type, inst_members.length, v.isOptional, v.valueType, ); inst_members.push(m); }); const inst_meta = new ObjectDescription( `@Union${typeId}`, ObjectDescriptionType.INTERFACE, ); inst_meta.members = inst_members; createObjectDescriptionShapes(context, inst_meta); return new ObjectType( context.nextTypeId(), inst_meta, ObjectTypeFlag.UNION, ); } export function createUnionType( context: BuildContext, element_types: Set, tsType?: Type, ): UnionType { const wide_type = CreateWideTypeFromTypes(context, element_types, tsType); return new UnionType(context.nextTypeId(), element_types, wide_type); } export function CreateWideTypeFromTypes( context: BuildContext, types: Set, tsType?: Type, ) { const wide_types = collectWideTypes(types); let wide_type = wide_types[0]; if (wide_types.length > 1) { const typeId = context.nextTypeId(); const inf_type = createUnionInterfaceType(typeId, wide_types, context); if (tsType) { context.module.typeByIds.set(typeId, inf_type); } wide_type = inf_type; } return wide_type; } function initTypeArguments( context: BuildContext, tstype: TSTypeWithArguments, value_type: ValueTypeWithArguments, ) { if (tstype.typeArguments) { for (const tp of tstype.typeArguments!) { const vtp = createType(context, tp) as TypeParameterType; value_type.addTypeParameter(vtp); } } } export function createType( context: BuildContext, type: Type, isObjectLiteral = false, ): ValueType { const module = context.module; let value_type = module.findValueTypeByType(type); if (value_type) return value_type; // do nothing switch (type.kind) { case TypeKind.CLASS: { value_type = createObjectType(type as TSClass, context)!; } break; case TypeKind.INTERFACE: { value_type = createObjectType(type as TSClass, context)!; } break; case TypeKind.ARRAY: { const arr = type as TSArray; const element_type = createType(context, arr.elementType); value_type = createArrayType(context, element_type, arr); break; } case TypeKind.FUNCTION: { const func = type as TSFunction; const retType = createType(context, func.returnType); const params: ValueType[] = []; for (const p of func.getParamTypes()) { params.push(createType(context, p)); } value_type = new FunctionType( context.nextTypeId(), retType, params, func.isOptionalParams, func.restParamIdx, ); break; } case TypeKind.UNION: { const unionType = type as TSUnion; const union_elements = new Set(); unionType.types.forEach((t) => union_elements.add(createType(context, t)), ); value_type = createUnionType(context, union_elements, type); break; } case TypeKind.TYPE_PARAMETER: { const type_param = type as TSTypeParameter; const wideType = createType(context, type_param.wideType); const defaultType = type_param.defaultType ? createType(context, type_param.defaultType) : undefined; value_type = new TypeParameterType( context.nextTypeId(), type_param.name, wideType, type_param.index, defaultType, ); break; } case TypeKind.ENUM: { const type_enum = type as TSEnum; const enum_type = new EnumType( context.nextTypeId(), `${context.getScopeNamespace()}|${type_enum.name}`, createType(context, type_enum.memberType), type_enum.members, ); context.module.enums.set(enum_type.name, enum_type); value_type = enum_type; break; } case TypeKind.TUPLE: { const tsTuple = type as TSTuple; const tuple_elements: ValueType[] = []; for (const element_type of tsTuple.elements) { tuple_elements.push(createType(context, element_type)); } const tuple_type = new TupleType( context.nextTypeId(), tuple_elements, ); value_type = tuple_type; break; } case TypeKind.CONTEXT: { const contextType = type as TSContext; const parentCtxType = contextType.parentCtxType ? (createType( context, contextType.parentCtxType, ) as ClosureContextType) : undefined; const freeVarTypeList: ValueType[] = []; for (const t of contextType.freeVarTypeList) { freeVarTypeList.push(createType(context, t)); } value_type = new ClosureContextType(parentCtxType, freeVarTypeList); break; } case TypeKind.WASM_ARRAY: { const wasmArrayType = type as WasmArrayType; const arrayValueType = createType( context, wasmArrayType.arrayType, ) as ArrayType; value_type = new WASMArrayType( arrayValueType, wasmArrayType.packedTypeKind, wasmArrayType.mutability, wasmArrayType.nullability, ); break; } case TypeKind.WASM_STRUCT: { const wasmStructType = type as WasmStructType; const structValueType = createType( context, wasmStructType.tupleType, ) as TupleType; value_type = new WASMStructType( structValueType, wasmStructType.packedTypeKinds, wasmStructType.mutabilitys, wasmStructType.nullability, wasmStructType.baseType ? (createType( context, wasmStructType.baseType, ) as WASMStructType) : undefined, ); break; } } if (value_type) { context.module.types.set(type, value_type); context.module.typeByIds.set(value_type.typeId, value_type); } else { value_type = Primitive.Any; } context.globalSymbols.set(type, value_type!); if ( type instanceof TSTypeWithArguments && value_type instanceof ValueTypeWithArguments ) { initTypeArguments( context, type as TSTypeWithArguments, value_type as ValueTypeWithArguments, ); } return value_type!; } function handleRecType( context: BuildContext, clazz: TSClass, inst_type: ObjectType, ) { /** the frontend will only generate single tsclass for object type, so here we can determine whether the type is * belong to rec group */ for (let i = 0; i < context.recClassTypeGroup.length; ++i) { const row = context.recClassTypeGroup[i]; const col = row.indexOf(clazz); if (col !== -1) { context.module.recObjectTypeGroup[i][col] = inst_type; } } } export function createObjectType( clazz: TSClass, context: BuildContext, ): ObjectType | undefined { const objectType = context.module.findObjectValueType(clazz); if (objectType) { return objectType as ObjectType; } // filter out useless class types if ( clazz.typeKind == TypeKind.CLASS && !clazz.isLiteral && !clazz.belongedScope ) { return undefined; } if (clazz.mangledName.includes(BuiltinNames.builtinTypeManglePrefix)) { if (IsBuiltinObject(clazz.className)) { return createBuiltinObjectType(clazz, context); } } let mangledName = clazz.mangledName; if (mangledName.length == 0) mangledName = clazz.className; const instName = mangledName; const clazzName = InternalNames.getClassMetaName(mangledName); const exist_type = context.getNamedValueType(instName); if (exist_type) return exist_type as ObjectType; let instance_type = ObjectDescriptionType.OBJECT_INSTANCE; if (clazz.isLiteral) instance_type = ObjectDescriptionType.OBJECT_LITERAL; else if (clazz.kind == TypeKind.INTERFACE) instance_type = ObjectDescriptionType.INTERFACE; const base_class = clazz.getBase(); const impl_infc = clazz.getImplInfc(); let base_type: ObjectType | undefined = undefined; let impl_type: ObjectType | undefined = undefined; if (base_class) base_type = createObjectType(base_class, context); if (impl_infc) impl_type = createObjectType(impl_infc, context); // TODO use className as instanceName const inst_meta = new ObjectDescription(instName, instance_type); let clazz_meta: ObjectDescription | undefined = undefined; if (base_type) inst_meta.base = base_type.instanceType!.meta; context.objectDescriptions.set(inst_meta.name, inst_meta); const inst_type = new ObjectType( clazz.typeId, inst_meta, clazz.isLiteral ? ObjectTypeFlag.LITERAL : ObjectTypeFlag.OBJECT, ); context.metaAndObjectTypeMap.set(inst_meta, inst_type); inst_type.implId = DefaultTypeId; if (impl_infc) { inst_type.implId = impl_infc.typeId; } else if (base_class) { let sup_class: TSClass | null = base_class; while (sup_class) { const temp_impl_infc = sup_class.getImplInfc(); if (temp_impl_infc) { inst_type.implId = temp_impl_infc.typeId; break; } sup_class = sup_class.getBase(); } } let genericOwner: ObjectType | undefined; if (clazz.genericOwner) { genericOwner = context.module.findValueTypeByType( clazz.genericOwner, ) as ObjectType; if (genericOwner) { inst_type.setGenericOwner(genericOwner.instanceType!); } } if (inst_meta.isObjectInstance) { clazz_meta = new ObjectDescription( clazzName, ObjectDescriptionType.OBJECT_CLASS, ); if (base_type) { clazz_meta.base = base_type.classType!.meta; inst_type.super = base_type; } inst_type.impl = impl_type ?? undefined; clazz_meta.instance = inst_meta; inst_meta.clazz = clazz_meta; context.objectDescriptions.set(clazzName, clazz_meta); const clazz_type = new ObjectType( clazz.typeId + 1, clazz_meta, ObjectTypeFlag.CLASS, ); context.metaAndObjectTypeMap.set(clazz_meta, clazz_type); clazz_type.implId = inst_type.implId; clazz_type.instanceType = inst_type; inst_type.classType = clazz_type; if (genericOwner) { clazz_type.setGenericOwner(genericOwner.classType!); } } // set the index signature if (clazz.numberIndexType) { inst_type.setNumberIndexType( createType(context, clazz.numberIndexType), ); } if (clazz.stringIndexType) { inst_type.setStringIndexType( createType(context, clazz.stringIndexType), ); } context.setNamedValueType(mangledName, inst_type); context.pushTask(() => updateMemberDescriptions(context, clazz, inst_meta, clazz_meta), ); handleRecType(context, clazz, inst_type); return inst_type; } function createBuiltinObjectType( clazz: TSClass, context: BuildContext, ): ObjectType { const obj_type = GetBuiltinObjectType(clazz.className); if (obj_type.meta.isInited) return obj_type; if (obj_type.isObject()) context.setNamedValueType(obj_type.meta.name, obj_type); if (clazz.typeKind == TypeKind.INTERFACE || clazz.isLiteral) context.pushTask(() => updateMemberDescriptions( context, clazz, obj_type.meta, undefined, false, ), ); else context.pushTask(() => updateMemberDescriptions( context, clazz, obj_type.instanceType!.meta, obj_type.classType!.meta, false, ), ); context.objectDescriptions.set(obj_type.meta.name, obj_type.meta); handleRecType(context, clazz, obj_type); return obj_type; } interface ClassMemberCount { static_count: number; inst_count: number; } function getClassMemberCount(clazz: TSClass): ClassMemberCount { let static_count = clazz.staticFields.length; let inst_count = clazz.fields.length; const is_interface = clazz.typeKind == TypeKind.INTERFACE; const accessor_names = new Set(); if (!is_interface && clazz.ctorType) { static_count++; } for (const m of clazz.memberFuncs) { if (m.type.funcKind == FunctionKind.CONSTRUCTOR) { /* This branch will never be reached since constructor has not been stored int members */ if (is_interface) inst_count++; else static_count++; } else if (m.type.funcKind == FunctionKind.STATIC) { static_count++; } else { if ( m.type.funcKind == FunctionKind.GETTER || m.type.funcKind == FunctionKind.SETTER ) { if (!accessor_names.has(m.name)) { inst_count++; accessor_names.add(m.name); } } else { inst_count++; } } } return { static_count, inst_count }; } function getGlobalFunction( context: BuildContext, globalName: string, ): Value | undefined { const value = context.getGlobalValue(globalName); if (value && (value as VarValue).ref instanceof FunctionDeclareNode) { return value as VarValue; } return undefined; } function updateMemberDescriptions( context: BuildContext, clazz: TSClass, inst_meta: ObjectDescription, clazz_meta?: ObjectDescription, is_base = false, ) { if (inst_meta.isInited) return; let inst_idx = 0; const is_interface = clazz.kind == TypeKind.INTERFACE; const base = clazz.getBase(); if (base) { updateMemberDescriptions( context, base, inst_meta.base!, clazz_meta?.base, true, ); inst_idx = inst_meta.base?.members.length || 0; } let inst_offset = inst_meta.base ? inst_meta.base!.fieldCount : 0; let class_idx = 0; let clazz_offset = 0; if (clazz_meta) { clazz_offset = clazz_meta.base ? clazz_meta.base!.fieldCount : 0; } const is_instance = !!clazz_meta; const counts = getClassMemberCount(clazz); inst_meta.members = new Array(counts.inst_count); if (clazz_meta) clazz_meta.members = new Array(counts.static_count); for (const f of clazz.fields) { const is_optional = f.optional == undefined ? false : f.optional; const value_type = createType(context, f.type); const member = inst_meta.updateMember( f.name, inst_idx, MemberType.FIELD, is_optional, value_type, true, ); member.offset = member.index; if (member.index === inst_idx) { inst_idx++; } } if (clazz_meta) { clazz_meta.setInited(); for (const f of clazz.staticFields) { const is_optional = f.optional == undefined ? false : f.optional; const value_type = createType(context, f.type); const staticInitExpr = clazz.staticFieldsInitValueMap.get(class_idx); let staticInitValue: SemanticsValue | undefined = undefined; if (staticInitExpr) { staticInitValue = buildExpression(staticInitExpr, context); if (!staticInitValue.type.equals(value_type)) { staticInitValue = newCastValue(value_type, staticInitValue); } } const member = clazz_meta.updateMember( f.name, class_idx++, MemberType.FIELD, is_optional, value_type, true, undefined, true, staticInitValue, ); member.offset = member.index; } } if (!is_base && clazz_meta) { const ctor = clazz.ctorType; if (ctor) { const ctor_type = createType(context, ctor); /* TODO: add ctor in a seperate field, can be deleted later */ const ctorMember = new MemberDescription( InternalNames.CONSTRUCTOR, MemberType.CONSTRUCTOR, -1, false, ctor_type, { method: getGlobalFunction( context, `${clazz.mangledName}|${InternalNames.CONSTRUCTOR}`, ), }, ); ctorMember.isDeclaredCtor = clazz.hasDeclareCtor; clazz_meta.ctor = ctorMember; if (clazz_meta.instance) { clazz_meta.instance.ctor = ctorMember; } /* put ctor into clazz members */ const member = clazz_meta.updateMember( InternalNames.CONSTRUCTOR, class_idx++, MemberType.CONSTRUCTOR, false, ctor_type, true, { method: getGlobalFunction( context, `${clazz.mangledName}|${InternalNames.CONSTRUCTOR}`, ), }, ); member.offset = clazz_offset++; } } for (const m of clazz.memberFuncs) { const value_type = createType(context, m.type); const is_optional = m.optional == undefined ? false : m.optional; Logger.debug( `=== member method:${m.name} ${m.type.funcKind} ${value_type}`, ); if (m.type.funcKind == FunctionKind.CONSTRUCTOR) { if (is_interface || (!is_base && clazz_meta)) { // add the constructor type const meta = is_interface ? inst_meta : clazz_meta!; const member = meta.updateMember( InternalNames.CONSTRUCTOR, is_interface ? inst_idx++ : class_idx++, MemberType.METHOD, is_optional, value_type, true, { method: getGlobalFunction( context, `${clazz.mangledName}|${InternalNames.CONSTRUCTOR}`, ), }, ); member.offset = is_interface ? inst_offset++ : clazz_offset++; } } else if (m.type.funcKind == FunctionKind.STATIC) { if (clazz_meta) { const is_override_or_own = clazz.overrideOrOwnMethods.has( m.name, ); const member = clazz_meta.updateMember( m.name, class_idx++, MemberType.METHOD, is_optional, value_type, is_override_or_own, { method: getGlobalFunction( context, `${clazz.mangledName}|@${m.name}`, ), }, true, ); member.offset = member.index; } } else if ( m.type.funcKind == FunctionKind.GETTER || m.type.funcKind == FunctionKind.SETTER ) { const is_setter = m.type.funcKind == FunctionKind.SETTER; const name = `${is_setter ? 'set_' : 'get_'}${m.name}`; const globalName = is_interface ? `${clazz.className}|${name}` : `${clazz.mangledName}|${name}`; const func = is_interface ? undefined : getGlobalFunction(context, globalName); const is_override_or_own = clazz.overrideOrOwnMethods.has(name); let accessor = inst_meta.findMember(m.name); const field_type = getAccessorType( globalName, value_type, is_setter, ); if (!accessor) { // get the result type accessor = inst_meta.updateMember( m.name, inst_idx, MemberType.ACCESSOR, is_optional, field_type, is_override_or_own, ); if (accessor.index === inst_idx) { inst_idx++; } } else { if ( accessor.valueType.kind == ValueTypeKind.ANY && field_type.kind != ValueTypeKind.ANY ) { accessor.valueType = field_type; } } if (is_setter) { accessor.setterType = field_type; } else { accessor.getterType = field_type; } if (func) { accessor.setAccessorFunction(func, is_setter); } else { // when 'func' is empty, it means that the current getter/setter is inherited from the base class if (!is_interface) { let baseClass = clazz.getBase(); while (baseClass) { const globalMethodName = `${baseClass.mangledName}|${name}`; const funcValue = getGlobalFunction( context, globalMethodName, ); if (funcValue) { if ( (is_setter && !accessor.hasSetter) || (!is_setter && !accessor.hasGetter) ) { accessor.setAccessorFunction( funcValue, is_setter, ); } break; } baseClass = baseClass.getBase(); } } } if (!is_instance) { if (is_setter) accessor.setterOffset = inst_offset; else accessor.getterOffset = inst_offset; inst_offset++; } } else { // method, in prototype const is_override_or_own = clazz.overrideOrOwnMethods.has(m.name); const member = inst_meta.updateMember( m.name, inst_idx, MemberType.METHOD, is_optional, value_type, is_override_or_own, is_interface ? undefined : { method: getGlobalFunction( context, `${clazz.mangledName}|${m.name}`, ), }, ); if (!is_instance) { member.offset = inst_offset++; } if (member.index === inst_idx) { inst_idx++; } } } inst_meta.fieldCount = inst_offset; if (clazz_meta) clazz_meta.fieldCount = clazz_offset; inst_meta.setInited(); } function getAccessorType( name: string, vt: ValueType, is_setter: boolean, ): ValueType { if (vt.kind == ValueTypeKind.FUNCTION) { const func_type = vt as FunctionType; let type = Primitive.Any; if (is_setter) { const params = func_type.argumentsType; // get the last params type = params[params.length - 1]; } else { type = func_type.returnType; } if ( !type || type.kind == ValueTypeKind.UNDEFINED || type.kind == ValueTypeKind.NULL || type.kind == ValueTypeKind.VOID ) { Logger.warn( `[WARNING] ${name} ${ is_setter ? 'SETTER' : 'GETTER' }: ${vt} need a right type ${type}`, ); return Primitive.Any; } return type; } return vt; } export function createObjectDescriptionShapes( context: BuildContext, meta: ObjectDescription, ) { if (meta.originShape) return; const shape = new Shape(meta); meta.originShape = shape; const members = new Array(meta.members.length); shape.members = members; const is_instance = meta.type == ObjectDescriptionType.OBJECT_INSTANCE; const is_Literal = meta.type == ObjectDescriptionType.OBJECT_LITERAL; for (let i = 0; i < meta.members.length; i++) { const m = meta.members[i]; if (is_instance || is_Literal) { if (m.type == MemberType.FIELD) { members[i] = new ShapeField(m.offset); } else if (m.methodOrAccessor) { members[i] = CreateShapeMemberValue(m, context); } } else { const sm = CreateShapeMember(m); if (sm) members[i] = sm; } } if (is_instance && meta.drived > 0) { const this_shape = new Shape(meta); const this_members = new Array(members.length); this_shape.members = this_members; for (let i = 0; i < members.length; i++) { if (members[i].kind == MemberType.FIELD) this_members[i] = members[i]; else { // TODO check private method } } meta.thisShape = this_shape; } else if (meta.type == ObjectDescriptionType.INTERFACE) { // create a shape with null members const this_shape = new Shape(meta); const this_members = new Array(members.length); this_shape.members = this_members; meta.thisShape = this_shape; } else { meta.thisShape = shape; } } function CreateShapeMember(member: MemberDescription): ShapeMember | undefined { switch (member.type) { case MemberType.FIELD: return new ShapeField(member.offset); case MemberType.ACCESSOR: return new ShapeAccessor( ShapeMemberStorage.OFFSET, member.getterOffset, member.setterOffset, ); case MemberType.METHOD: return new ShapeMethod(ShapeMemberStorage.OFFSET, member.offset); } return undefined; } function CreateShapeMemberValue( member: MemberDescription, context: BuildContext, ): ShapeMember | undefined { switch (member.type) { case MemberType.ACCESSOR: { return new ShapeAccessor( ShapeMemberStorage.VALUE, member.getter, member.setter, ); } case MemberType.METHOD: return new ShapeMethod(ShapeMemberStorage.VALUE, member.method); } return undefined; } function specializeValue( mapper: SpecializeTypeMapper, value?: Value, ): Value | undefined { if (!value) return undefined; if (value instanceof VarValue) { const var_value = value as VarValue; return new VarValue( var_value.kind as VarValueKind, mapper.getValueType(var_value.type), var_value.ref, var_value.index, ); } return value; } function specializeMemberDescription( m: MemberDescription, mapper: SpecializeTypeMapper, ): MemberDescription { const methodOrAccessor = m.methodOrAccessor; let newMemberOrAccessor: MemberOrAccessor | undefined = undefined; if (methodOrAccessor) { newMemberOrAccessor = {}; newMemberOrAccessor!.method = specializeValue( mapper, methodOrAccessor.method, ); newMemberOrAccessor!.getter = specializeValue( mapper, methodOrAccessor.getter, ); newMemberOrAccessor!.setter = specializeValue( mapper, methodOrAccessor.setter, ); } return new MemberDescription( m.name, m.type, m.index, m.isOptional, mapper.getValueType(m.valueType), newMemberOrAccessor, ); } function simpleCopyShape( meta: ObjectDescription, shape?: Shape, ): Shape | undefined { if (shape) { const new_shape = new Shape(meta, shape); new_shape.members = shape.members; return new_shape; } return undefined; } function initSpecializeObjectDescription( mapper: SpecializeTypeMapper, meta: ObjectDescription, self_meta: ObjectDescription, ) { self_meta.base = meta.base; self_meta.fieldCount = meta.fieldCount; self_meta.drived = meta.drived; self_meta.originShape = simpleCopyShape(self_meta, meta.originShape); self_meta.thisShape = meta.thisShape === meta.originShape ? self_meta.originShape : simpleCopyShape(self_meta, meta.thisShape); self_meta.setInited(); self_meta.members = []; // init all members for (const m of meta.members) { self_meta.members.push(specializeMemberDescription(m, mapper)); } if (meta.isObjectClass) { const inst_meta = specializeObjectDescription(mapper, meta.instance); self_meta.instance = inst_meta; if (inst_meta) inst_meta.clazz = self_meta; } } function specializeObjectDescription( mapper: SpecializeTypeMapper, meta?: ObjectDescription, ): ObjectDescription | undefined { if (!meta) return undefined; const self_meta = new ObjectDescription(meta!.name, meta!.type, meta!); if (meta.isBuiltin) self_meta.setBuiltin(); if (meta.isInited && meta.originShape) initSpecializeObjectDescription(mapper, meta!, self_meta); return self_meta; } function specializeIndexType( mapper: SpecializeTypeMapper, obj_type: ObjectType, indexType: ValueType, ): ValueType { if ( indexType.kind == ValueTypeKind.TYPE_PARAMETER && obj_type.inTypeArguments(indexType) ) { return mapper.getValueType(indexType); } return mapper.getValueType(indexType); } function initIndexType( mapper: SpecializeTypeMapper, obj_type: ObjectType, special_type: ObjectType, ) { if (obj_type.numberIndexType) { special_type.setNumberIndexType( specializeIndexType(mapper, obj_type, obj_type.numberIndexType), ); } if (obj_type.stringIndexType) { special_type.setStringIndexType( specializeIndexType(mapper, obj_type, obj_type.stringIndexType), ); } } export class SpecializeTypeMapper { typeMap = new Map(); specializedMap = new Map(); ownerType: ValueTypeWithArguments; constructor(owner: ValueTypeWithArguments, typeArgs: ValueType[]) { this.ownerType = owner; this.initTypeArguments(typeArgs); } initTypeArguments(typeArgs: ValueType[]) { const typeTypeParams = this.ownerType.typeArguments; if (!typeTypeParams || typeTypeParams.length == 0) return; let i = 0; for (; i < typeTypeParams.length && i < typeArgs.length; i++) { this.typeMap.set(typeTypeParams[i], typeArgs[i]); } for (; i < typeTypeParams.length; i++) { const def_type = typeTypeParams[i].defaultType; if (def_type) { this.typeMap.set(typeTypeParams[i], def_type); } } } getValueType(valueType: ValueType): ValueType { if (valueType.kind == ValueTypeKind.TYPE_PARAMETER) { const vt = this.typeMap.get(valueType as TypeParameterType); if (vt) return vt; return valueType; } const vt = this.specializedMap.get(valueType); if (vt) { return vt; } return this.specializeValueType(valueType); } updateObjectSpecialTypeArguments(obj_type: ObjectType) { const typeArguments = (obj_type.genericOwner! as ObjectType) .typeArguments; if (typeArguments) { const typeArgs: ValueType[] = []; for (const type of typeArguments) { const special_type = this.getValueType(type); typeArgs.push(special_type); } obj_type.setSpecialTypeArguments(typeArgs); } } updateObjectTypeArguments(obj_type: ObjectType, special_type: ObjectType) { initIndexType(this, obj_type, special_type); if (special_type != obj_type) { special_type.setGenericOwner(obj_type); if (obj_type.typeArguments) { special_type.setTypeArguments(obj_type.typeArguments!); } } } specializeValueType(valueType: ValueType): ValueType { let special_type: ValueType = valueType; switch (valueType.kind) { case ValueTypeKind.ARRAY: case ValueTypeKind.OBJECT: { let obj_type = valueType as ObjectType; if ( obj_type.isClassObject() || (obj_type.isObject() && obj_type.classType) ) { if (obj_type.isObject()) { obj_type = obj_type.classType!; } const self_meta = new ObjectDescription( obj_type.meta.name, obj_type.meta.type, obj_type.meta, ); const self = obj_type.clone( DefaultTypeId, self_meta, obj_type.flags, obj_type.implId, ); // create instance const self_inst_meta = new ObjectDescription( obj_type.instanceType!.meta.name, obj_type.instanceType!.meta.type, obj_type.instanceType!.meta, ); const inst = obj_type.instanceType!.clone( DefaultTypeId, self_inst_meta, ObjectTypeFlag.OBJECT, obj_type.instanceType!.implId, ); inst.classType = self; self.instanceType = inst; self_inst_meta.clazz = self_meta; self_meta.instance = self_inst_meta; self.setGenericOwner(obj_type); inst.setGenericOwner(obj_type.instanceType!); this.specializedMap.set(obj_type, self); this.specializedMap.set(obj_type.instanceType!, inst); this.updateObjectSpecialTypeArguments(self); this.updateObjectSpecialTypeArguments(inst); special_type = inst; if (obj_type.meta.isInited && obj_type.meta.originShape) initSpecializeObjectDescription( this, obj_type.meta, self_meta, ); if ( obj_type.instanceType!.meta.isInited && obj_type.instanceType!.meta.originShape ) initSpecializeObjectDescription( this, obj_type.instanceType!.meta, self_inst_meta, ); } else { const self_meta = new ObjectDescription( obj_type.meta.name, obj_type.meta.type, obj_type.meta, ); special_type = obj_type.clone( DefaultTypeId, self_meta, obj_type.flags, obj_type.implId, ); special_type.setGenericOwner(obj_type); this.specializedMap.set(obj_type, special_type); this.updateObjectSpecialTypeArguments( special_type as ObjectType, ); if (obj_type.meta.isInited && obj_type.meta.originShape) initSpecializeObjectDescription( this, obj_type.meta, self_meta, ); } initIndexType( this, valueType as ObjectType, special_type as ObjectType, ); break; } case ValueTypeKind.FUNCTION: { const func_type = valueType as FunctionType; const ret_type = this.getValueType(func_type.returnType); let need_special = !ret_type.equals(func_type.returnType); const args: ValueType[] = []; for (const p of func_type.argumentsType) { const arg = this.getValueType(p); need_special = need_special || !arg.equals(p); args.push(arg); } if (need_special) { special_type = new FunctionType( -1, ret_type, args, func_type.isOptionalParams, func_type.restParamIdx, ); this.specializedMap.set(valueType, special_type); } break; } } if (special_type !== valueType) { special_type.setGenericOwner(valueType); if (valueType.isBuiltin) { special_type.setBuiltin(); } if (special_type instanceof ValueTypeWithArguments) { if (special_type !== this.ownerType) { if ((valueType as ValueTypeWithArguments).typeArguments) ( special_type as ValueTypeWithArguments ).setTypeArguments( (valueType as ValueTypeWithArguments) .typeArguments!, ); } } } return special_type; } } function initBuiltinObjectTypeDescriptions( mapper: SpecializeTypeMapper, type: ObjectType, special_type: ObjectType, typeArgs: ValueType[], need_update_type_args: boolean, ) { if (need_update_type_args) { mapper.initTypeArguments(typeArgs); mapper.updateObjectTypeArguments(type, special_type); } initSpecializeObjectDescription( mapper, type.instanceType!.meta, special_type.instanceType!.meta, ); initSpecializeObjectDescription( mapper, type.classType!.meta, special_type.classType!.meta, ); } export function specializeBuiltinObjectType( name: string, typeArgs: ValueType[], ): ObjectType | undefined { const type = GetBuiltinObjectType(name); if (!type) return undefined; if (!type.typeArguments) return type; const mapper = new SpecializeTypeMapper(type, typeArgs); // Array Type: may get ArrayType before define the ArrayType const need_update_type_args = type.hasUninitedTypeArguments; const special_type = mapper.specializeValueType(type) as ObjectType; // before the array is specialized, mark the actual element type of the array if (special_type instanceof ArrayType) { special_type.setElement(typeArgs[0]); } if (special_type !== type) { if (!(type.meta.isInited && type.meta.originShape)) { AddSpecializeObjectType(type, () => { initBuiltinObjectTypeDescriptions( mapper, type, special_type, typeArgs, need_update_type_args, ); }); } else { initBuiltinObjectTypeDescriptions( mapper, type, special_type, typeArgs, need_update_type_args, ); } return special_type; } return type; } export function needSpecialized(type: ValueType) { if (type instanceof TypeParameterType && type.specialTypeArgument) { return true; } if (type instanceof ValueTypeWithArguments && type.specialTypeArguments) { return true; } return false; } ================================================ FILE: src/semantics/value.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import ts from 'typescript'; import { DumpWriter } from './dump.js'; import { ValueTypeKind, ValueType, PrimitiveType, Primitive, PrimitiveValueType, FunctionType, UnionType, ArrayType, TypeParameterType, ObjectType, WASMType, WASMArrayType, } from './value_types.js'; import { PredefinedTypeId, SourceLocation } from '../utils.js'; import { SymbolKeyToString } from './builder_context.js'; import { FunctionDeclareNode, VarDeclareNode } from './semantics_nodes.js'; import { Shape, ShapeMember, Value } from './runtime.js'; import { GetPredefinedType } from './predefined_types.js'; import { GetShapeFromType } from './builtin.js'; export enum SemanticsValueKind { NOP, UNIMPLEMENT, // terminal symbol THIS, SUPER, LITERAL, LOCAL_VAR, LOCAL_CONST, PARAM_VAR, GLOBAL_VAR, GLOBAL_CONST, CLOSURE_VAR, CLOSURE_CONST, TEMPL_VAR, // expression BINARY_EXPR, POST_UNARY_EXPR, PRE_UNARY_EXPR, CONDITION_EXPR, COMMA_EXPR, FUNCTION_CALL, CLOSURE_CALL, CONSTRUCTOR_CALL, ANY_CALL, NEW_CLASS, NEW_CLOSURE_FUNCTION, OBJECT_CAST_OBJECT, OBJECT_CAST_VALUE, // cast to boolean OBJECT_CAST_UNION, VALUE_CAST_ANY, VALUE_CAST_UNION, OBJECT_CAST_ANY, VALUE_CAST_VALUE, VALUE_CAST_OBJECT, // null/undefined to object ANY_CAST_VALUE, ANY_CAST_OBJECT, ANY_CAST_INTERFACE, UNION_CAST_VALUE, UNION_CAST_OBJECT, UNION_CAST_ANY, VALUE_TO_STRING, OBJECT_TO_STRING, INSTANCE_OF, ENUM_KEY_GET, DYNAMIC_GET, DYNAMIC_SET, DYNAMIC_CALL, SHAPE_GET, SHAPE_SET, SHAPE_CALL, OFFSET_GET, OFFSET_SET, OFFSET_GETTER, OFFSET_SETTER, OFFSET_CALL, VTABLE_GET, VTABLE_SET, VTABLE_CALL, DIRECT_GETTER, DIRECT_SETTER, DIRECT_CALL, DIRECT_GET, ENUMERATE_KEY_GET, STRING_INDEX_GET, STRING_INDEX_SET, ARRAY_INDEX_GET, ARRAY_INDEX_SET, OBJECT_INDEX_GET, OBJECT_INDEX_SET, TUPLE_INDEX_GET, TUPLE_INDEX_SET, WASMARRAY_INDEX_GET, WASMARRAY_INDEX_SET, WASMSTRUCT_INDEX_GET, WASMSTRUCT_INDEX_SET, OBJECT_KEY_GET, OBJECT_KEY_SET, NEW_CONSTRCTOR_OBJECT, NEW_LITERAL_OBJECT, NEW_LITERAL_ARRAY, NEW_ARRAY, NEW_ARRAY_LEN, NEW_FROM_CLASS_OBJECT, NEW_RAW_ARRAY, NEW_RAW_ARRAY_LEN, // FOR flatten BLOCK, BLOCK_BRANCH, BLOCK_BRANCH_IF, RET, TYPEOF, TEMPLATE_EXPRESSION, REBINDING, SPREAD, } export type ValueBinaryOperator = ts.BinaryOperator; export interface SemanticsValueVisitor { (value: SemanticsValue): void; } export class SemanticsValue implements Value { constructor(public kind: SemanticsValueKind, public type: ValueType) {} toString(): string { return `[${SemanticsValueKind[this.kind]} ${this.type}]`; } location: SourceLocation | null = null; dump(writer: DumpWriter) { writer.write(this.toString()); writer.shift(); this.forEachChild((v) => v.dump(writer)); writer.unshift(); } get effectType(): ValueType { if (this.type.kind == ValueTypeKind.TYPE_PARAMETER) return (this.type as TypeParameterType).wideType; return this.type; } forEachChild(visitor: SemanticsValueVisitor) { return; } private _shape?: Shape; get shape(): Shape | undefined { // TODO get the basic shape return this._shape ? this._shape : GetShapeFromType(this.effectType); } set shape(s: Shape | undefined) { this._shape = s; } get valueAccessCount(): number { return 0; } incAccessCount() { return; } } export class ThisValue2 extends SemanticsValue { constructor(type: ObjectType) { super(SemanticsValueKind.THIS, type); this.shape = type.instanceType!.meta.thisShape; } } /* When we use the ”super" keyword, there are two situations: 1. super(...), this means that the constructor of the base class needs to be called; e.g. class B extends A { constructor(x: number, y: string) { super(x); } } 2. super.xxx, at this time, "super" represents the base class. e.g. class B extends A { log() { super.log(); } } So, we use "SuperUsageFlag" to distinguish between these two situations. */ export enum SuperUsageFlag { SUPER_CALL, SUPER_LITERAL, } export class SuperValue extends SemanticsValue { private _parameters: SemanticsValue[] | undefined; private _usageFlag = SuperUsageFlag.SUPER_CALL; constructor( type: ObjectType, usageFlag: SuperUsageFlag, parameters?: SemanticsValue[], ) { super(SemanticsValueKind.SUPER, type); this.shape = type.instanceType!.meta.originShape; this._parameters = parameters; this._usageFlag = usageFlag; } get parameters(): SemanticsValue[] | undefined { return this._parameters; } get usageFlag(): SuperUsageFlag { return this._usageFlag; } } export class NopValue extends SemanticsValue { constructor() { super(SemanticsValueKind.NOP, Primitive.Void); } } export class LiteralValue extends SemanticsValue { constructor( type: PrimitiveType | WASMType, public value: PrimitiveValueType, ) { super(SemanticsValueKind.LITERAL, type); } toString(): string { return `[Literal ${this.type} ${this.value}]`; } } export type LocalVarValueKind = | SemanticsValueKind.LOCAL_VAR | SemanticsValueKind.LOCAL_CONST; export type GlobalVarValueKind = | SemanticsValueKind.GLOBAL_VAR | SemanticsValueKind.GLOBAL_CONST; export type ClosureVarValueKind = | SemanticsValueKind.CLOSURE_VAR | SemanticsValueKind.CLOSURE_CONST; export type VarValueKind = | LocalVarValueKind | GlobalVarValueKind | ClosureVarValueKind | SemanticsValueKind.PARAM_VAR; function VarRefToString(ref: any): string { if (ref instanceof ValueType) { return (ref as ValueType).toString(); } else if (ref instanceof FunctionDeclareNode) { return (ref as FunctionDeclareNode).toString(); } else if (ref instanceof VarDeclareNode) { return (ref as VarDeclareNode).toString(); } return `${ref}`; } export class VarValue extends SemanticsValue { constructor( kind: VarValueKind, type: ValueType, public ref: any, public index: number | string, ) { super(kind, type); if (type instanceof ObjectType) { this.shape = (type as ObjectType).meta.thisShape; } } copy() { const newVarValue = new VarValue( this.kind as VarValueKind, this.type, this.ref, this.index, ); newVarValue.shape = this.shape; return newVarValue; } get isConst(): boolean { return ( this.kind == SemanticsValueKind.LOCAL_CONST || this.kind == SemanticsValueKind.GLOBAL_CONST || this.kind == SemanticsValueKind.CLOSURE_CONST ); } toString(): string { return `[VarValue(${SemanticsValueKind[this.kind]}): ${this.type} ${ this.index } "${VarRefToString(this.ref)}"]`; } private _valueAccessCount = 0; get valueAccessCount(): number { return this._valueAccessCount; } incAccessCount() { this._valueAccessCount++; } } export class BinaryExprValue extends SemanticsValue { constructor( type: ValueType, public opKind: ValueBinaryOperator, public left: SemanticsValue, public right: SemanticsValue, ) { super(SemanticsValueKind.BINARY_EXPR, type); } toString(): string { return `[BinaryExpr "${operatorString(this.opKind)}" ${this.type}]`; } dump(writer: DumpWriter) { writer.write(`[BinaryExpr "${operatorString(this.opKind)}"]`); writer.shift(); this.left.dump(writer); this.right.dump(writer); writer.unshift(); } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.left); visitor(this.right); } } export class PrefixUnaryExprValue extends SemanticsValue { constructor( type: PrimitiveType, public opKind: ts.PrefixUnaryOperator, public target: SemanticsValue, flattenExprValue?: SemanticsValue, ) { super(SemanticsValueKind.PRE_UNARY_EXPR, type); if (flattenExprValue) { this._flattenExprValue = flattenExprValue; } } private _flattenExprValue?: SemanticsValue; setFlattenExprValue(flattenExprValue: SemanticsValue) { this._flattenExprValue = flattenExprValue; } get flattenExprValue(): SemanticsValue | undefined { return this._flattenExprValue; } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.target); } } export class PostUnaryExprValue extends SemanticsValue { constructor( type: PrimitiveType, public opKind: ts.PostfixUnaryOperator, public target: SemanticsValue, flattenExprValue?: SemanticsValue, ) { super(SemanticsValueKind.POST_UNARY_EXPR, type); if (flattenExprValue) { this._flattenExprValue = flattenExprValue; } } private _flattenExprValue?: SemanticsValue; setFlattenExprValue(flattenExprValue: SemanticsValue) { this._flattenExprValue = flattenExprValue; } get flattenExprValue(): SemanticsValue | undefined { return this._flattenExprValue; } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.target); } } export class ConditionExprValue extends SemanticsValue { constructor( type: ValueType, public condition: SemanticsValue, public trueExpr: SemanticsValue, public falseExpr: SemanticsValue, ) { super(SemanticsValueKind.CONDITION_EXPR, type); } dump(writer: DumpWriter) { writer.write(`[Condition]`); writer.shift(); this.condition.dump(writer); this.trueExpr.dump(writer); this.falseExpr.dump(writer); writer.unshift(); } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.condition); visitor(this.trueExpr); visitor(this.falseExpr); } } export class CommaExprValue extends SemanticsValue { constructor(type: ValueType, public exprs: SemanticsValue[]) { super(SemanticsValueKind.COMMA_EXPR, type); } dump(writer: DumpWriter) { writer.write(`[CommaExpr]`); writer.shift(); for (const expr of this.exprs) { expr.dump(writer); writer.write(', '); } writer.unshift(); } forEachChild(visitor: SemanticsValueVisitor) { this.exprs.forEach((expr) => visitor(expr)); } } export class FunctionCallBaseValue extends SemanticsValue { constructor( kind: SemanticsValueKind, type: ValueType, public funcType: FunctionType, public parameters?: SemanticsValue[], ) { super(kind, type); } private _typeArguments?: ValueType[]; setTypeArguments(typeArgs: ValueType[]) { this._typeArguments = typeArgs; } get typeArguments(): ValueType[] | undefined { return this._typeArguments; } forEachChild(visitor: SemanticsValueVisitor) { if (this.parameters) { for (const p of this.parameters) visitor(p); } } } export class FunctionCallValue extends FunctionCallBaseValue { constructor( type: ValueType, public func: SemanticsValue, funcType?: FunctionType, parameters?: SemanticsValue[], ) { super( SemanticsValueKind.FUNCTION_CALL, type, funcType ? funcType : (func.type as FunctionType), parameters, ); } dump(writer: DumpWriter) { writer.write(`[FunctionCall return ${this.type}]`); writer.shift(); this.func.dump(writer); if (this.parameters) { for (const p of this.parameters) p.dump(writer); } writer.unshift(); } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.func); if (this.parameters) this.parameters.forEach((p) => visitor(p)); } } export class EnumerateKeysGetValue extends SemanticsValue { constructor(public type: ValueType, public obj: SemanticsValue) { super(SemanticsValueKind.ENUMERATE_KEY_GET, type); } dump(writer: DumpWriter) { writer.write(`[EnumerateKeysGetValue]`); writer.shift(); this.obj.dump(writer); writer.unshift(); } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.obj); } } export class NewClosureFunction extends SemanticsValue { constructor( public readonly funcNode: FunctionDeclareNode, public readonly closureInitList?: VarValue[], ) { super(SemanticsValueKind.NEW_CLOSURE_FUNCTION, funcNode.funcType); } toString(): string { return `[NewClosureFunction ${this.funcNode}]`; } forEachChild(visitor: SemanticsValueVisitor) { if (this.closureInitList) { for (const p of this.closureInitList) { if (p) visitor(p); } } } } export class ClosureCallValue extends FunctionCallBaseValue { constructor( type: ValueType, public func: SemanticsValue, funcType?: FunctionType, parameters?: SemanticsValue[], ) { super( SemanticsValueKind.CLOSURE_CALL, type, funcType ? funcType : (func.type as FunctionType), parameters, ); } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.func); if (this.parameters) this.parameters.forEach((p) => visitor(p)); } } export class ConstructorCallValue extends FunctionCallBaseValue { constructor( public self: SemanticsValue, public ctr: SemanticsValue, funcType: FunctionType, parameters?: SemanticsValue[], ) { super( SemanticsValueKind.CONSTRUCTOR_CALL, Primitive.Void, funcType, parameters, ); } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.self); if (this.parameters) this.parameters.forEach((p) => visitor(p)); } } export class AnyCallValue extends SemanticsValue { constructor( type: ValueType, public anyFunc: SemanticsValue, public parameters?: SemanticsValue[], ) { super(SemanticsValueKind.ANY_CALL, type); } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.anyFunc); if (this.parameters) this.parameters.forEach((p) => visitor(p)); } } export type ElementGetValueKind = | SemanticsValueKind.STRING_INDEX_GET | SemanticsValueKind.ARRAY_INDEX_GET | SemanticsValueKind.OBJECT_KEY_GET | SemanticsValueKind.ENUM_KEY_GET | SemanticsValueKind.TUPLE_INDEX_GET | SemanticsValueKind.WASMARRAY_INDEX_GET | SemanticsValueKind.WASMSTRUCT_INDEX_GET; export class ElementGetValue extends SemanticsValue { constructor( kind: ElementGetValueKind, type: ValueType, public owner: SemanticsValue, public index: SemanticsValue, ) { super(kind, type); } dump(writer: DumpWriter) { writer.write( `[ElementGet(${SemanticsValueKind[this.kind]}) ${this.type}]`, ); writer.shift(); this.owner.dump(writer); this.index.dump(writer); writer.unshift(); } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.owner); visitor(this.index); } } export type ElementSetValueKind = | SemanticsValueKind.STRING_INDEX_SET | SemanticsValueKind.ARRAY_INDEX_SET | SemanticsValueKind.OBJECT_KEY_SET | SemanticsValueKind.TUPLE_INDEX_SET | SemanticsValueKind.WASMARRAY_INDEX_SET | SemanticsValueKind.WASMSTRUCT_INDEX_SET; export class ElementSetValue extends SemanticsValue { constructor( kind: ElementSetValueKind, type: ValueType, public owner: SemanticsValue, public index: SemanticsValue, public value?: SemanticsValue, public opKind?: ValueBinaryOperator, ) { super(kind, type); } dump(writer: DumpWriter) { writer.write( `[ElementSet(${SemanticsValueKind[this.kind]}) ${this.type} ${ this.opKind ? operatorString(this.opKind) : 'Unkonwn' }]`, ); writer.shift(); this.owner.dump(writer); this.index.dump(writer); if (this.value) this.value.dump(writer); writer.unshift(); } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.owner); visitor(this.index); if (this.value) visitor(this.value); } } type CastValueKind = | SemanticsValueKind.VALUE_CAST_VALUE | SemanticsValueKind.VALUE_CAST_OBJECT | SemanticsValueKind.VALUE_CAST_ANY | SemanticsValueKind.VALUE_CAST_UNION | SemanticsValueKind.OBJECT_CAST_OBJECT | SemanticsValueKind.OBJECT_CAST_VALUE | SemanticsValueKind.OBJECT_CAST_UNION | SemanticsValueKind.OBJECT_CAST_ANY | SemanticsValueKind.UNION_CAST_VALUE | SemanticsValueKind.UNION_CAST_OBJECT | SemanticsValueKind.UNION_CAST_ANY | SemanticsValueKind.ANY_CAST_INTERFACE | SemanticsValueKind.ANY_CAST_OBJECT | SemanticsValueKind.ANY_CAST_VALUE; export class CastValue extends SemanticsValue { isSigned = true; constructor( kind: CastValueKind, type: ValueType, public value: SemanticsValue, ) { super(kind, type); } dump(writer: DumpWriter) { const vt_str = 'DYNAMIC_CAST'; writer.write( `[CastValue(${SemanticsValueKind[this.kind]}) From "${ this.value.type }" To "${this.type}"]`, ); writer.shift(); this.value.dump(writer); writer.unshift(); } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.value); } } export class InstanceOfValue extends SemanticsValue { constructor( public value: SemanticsValue, public classObject: SemanticsValue, ) { super(SemanticsValueKind.INSTANCE_OF, Primitive.Boolean); } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.value); } } export class NewClassValue extends SemanticsValue { constructor(type: ValueType) { super(SemanticsValueKind.NEW_CLASS, type); } } export type ToStringValueKind = | SemanticsValueKind.VALUE_TO_STRING | SemanticsValueKind.OBJECT_TO_STRING; export class ToStringValue extends SemanticsValue { constructor(kind: ToStringValueKind, public value: SemanticsValue) { super(kind, Primitive.String); } dump(writer: DumpWriter) { writer.write(`[ToString]`); writer.shift(); this.value.dump(writer); writer.unshift(); } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.value); } } /** for typeof, iff value is any type, which can't be determined in compile time, so here * create TypeofValue to determine the type in runtime */ export class TypeofValue extends SemanticsValue { constructor(public value: SemanticsValue) { super(SemanticsValueKind.TYPEOF, Primitive.String); } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.value); } } export class TemplateExprValue extends SemanticsValue { constructor(public head: SemanticsValue, public follows: SemanticsValue[]) { super(SemanticsValueKind.TEMPLATE_EXPRESSION, Primitive.String); } forEachChild(visitor: SemanticsValueVisitor): void { visitor(this.head); (this.follows || []).forEach((v) => visitor(v)); } } export class UnimplementValue extends SemanticsValue { constructor(public tsNode: ts.Node) { super(SemanticsValueKind.UNIMPLEMENT, Primitive.Void); } toString(): string { const sourceFile = this.tsNode.getSourceFile(); const start = this.tsNode.getStart(sourceFile); const startLineInfo = sourceFile.getLineAndCharacterOfPosition(start); const source_info = `@"${sourceFile.fileName}":${ startLineInfo.line + 1 }:${startLineInfo.character} source: ${this.tsNode.getFullText( sourceFile, )}`; return `[Unimplement ExpressionKind: ${ ts.SyntaxKind[this.tsNode.kind] } ${source_info}]`; } } export class DynamicGetValue extends SemanticsValue { /* 'isMethodCall' determines the specific semantics of member function property access, whether it is just to obtain the member function property value, or needs to call the member function. */ constructor( public owner: SemanticsValue, public name: string, public isMethodCall: boolean, type?: ValueType, ) { super(SemanticsValueKind.DYNAMIC_GET, type ? type : Primitive.Any); } toString(): string { return `[DynamicGet ${this.name}]`; } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.owner); super.forEachChild(visitor); } } export class DynamicSetValue extends SemanticsValue { constructor( public owner: SemanticsValue, public name: string, public value?: SemanticsValue, public opKind?: ValueBinaryOperator, ) { super(SemanticsValueKind.DYNAMIC_SET, Primitive.Any); } toString(): string { return `[DynamicSet ${this.name}]`; } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.owner); super.forEachChild(visitor); } } export class DynamicCallValue extends FunctionCallBaseValue { constructor(public owner: SemanticsValue, public name: string) { const methodType = GetPredefinedType( PredefinedTypeId.FUNC_ANY_ARRAY_ANY_DEFAULT, )!; super( SemanticsValueKind.DYNAMIC_CALL, Primitive.Any, methodType as FunctionType, ); } toString(): string { return `[DynamicCall ${this.name}]`; } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.owner); super.forEachChild(visitor); } } export class ShapeGetValue extends SemanticsValue { constructor( public owner: SemanticsValue, type: ValueType, public index: number, ) { super(SemanticsValueKind.SHAPE_GET, type); } toString(): string { return `[ShapeGet ${this.index}]`; } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.owner); super.forEachChild(visitor); } } export class ShapeSetValue extends SemanticsValue { constructor( public owner: SemanticsValue, type: ValueType, public index: number, public value?: SemanticsValue, public opKind?: ValueBinaryOperator, ) { super(SemanticsValueKind.SHAPE_SET, type); } toString(): string { return `[ShapeSet ${this.index}]`; } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.owner); super.forEachChild(visitor); } } export class ShapeCallValue extends FunctionCallBaseValue { constructor( public owner: SemanticsValue, funcType: FunctionType, public index: number, ) { super(SemanticsValueKind.SHAPE_CALL, funcType.returnType, funcType); } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.owner); super.forEachChild(visitor); } toString(): string { return `[ShapeCall RET: ${this.type} OF ${this.owner}@${this.index}]`; } } export class OffsetGetValue extends SemanticsValue { constructor( public owner: SemanticsValue, type: ValueType, public index: number, ) { super(SemanticsValueKind.OFFSET_GET, type); } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.owner); super.forEachChild(visitor); } } export class OffsetSetValue extends SemanticsValue { constructor( public owner: SemanticsValue, type: ValueType, public index: number, public value?: SemanticsValue, public opKind?: ValueBinaryOperator, ) { super(SemanticsValueKind.OFFSET_SET, type); } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.owner); super.forEachChild(visitor); } } export class OffsetGetterValue extends SemanticsValue { constructor( public owner: SemanticsValue, type: ValueType, public index: number, ) { super(SemanticsValueKind.OFFSET_GETTER, type); } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.owner); super.forEachChild(visitor); } } export class OffsetSetterValue extends SemanticsValue { constructor( public owner: SemanticsValue, type: ValueType, public index: number, public getterIndex?: number, // for +=, *=, /= ... public value?: SemanticsValue, public opKind?: ValueBinaryOperator, ) { super(SemanticsValueKind.OFFSET_SETTER, type); } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.owner); super.forEachChild(visitor); } } export class OffsetCallValue extends FunctionCallBaseValue { constructor( public owner: SemanticsValue, funcType: FunctionType, public index: number, ) { super(SemanticsValueKind.OFFSET_CALL, funcType.returnType, funcType); } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.owner); super.forEachChild(visitor); } toString(): string { return `[OffsetCall ${this.funcType}@${this.index}]`; } } export class VTableGetValue extends SemanticsValue { constructor( public owner: SemanticsValue, type: ValueType, public index: number, ) { super(SemanticsValueKind.VTABLE_GET, type); } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.owner); super.forEachChild(visitor); } } export class VTableSetValue extends SemanticsValue { constructor( public owner: SemanticsValue, type: ValueType, public index: number, public value?: SemanticsValue, public opKind?: ValueBinaryOperator, ) { super(SemanticsValueKind.VTABLE_SET, type); } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.owner); super.forEachChild(visitor); } } export class VTableCallValue extends FunctionCallBaseValue { constructor( public owner: SemanticsValue, funcType: FunctionType, public index: number, ) { super(SemanticsValueKind.VTABLE_CALL, funcType.returnType, funcType); } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.owner); super.forEachChild(visitor); } } export class DirectGetterValue extends SemanticsValue { constructor( public owner: SemanticsValue, type: ValueType, public getter: Value, ) { super(SemanticsValueKind.DIRECT_GETTER, type); } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.owner); super.forEachChild(visitor); } } export class DirectCallValue extends FunctionCallBaseValue { constructor( public owner: SemanticsValue, methodType: FunctionType, public method: Value, ) { super( SemanticsValueKind.DIRECT_CALL, methodType.returnType, methodType, ); } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.owner); super.forEachChild(visitor); } } export class DirectSetterValue extends SemanticsValue { constructor( public owner: SemanticsValue, type: ValueType, public setter: Value, public getter?: Value, // for +=, *=, ... public value?: SemanticsValue, public opKind?: ValueBinaryOperator, ) { super(SemanticsValueKind.DIRECT_SETTER, type); } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.owner); super.forEachChild(visitor); } } export class DirectGetValue extends SemanticsValue { constructor( public owner: SemanticsValue, type: ValueType, public index: number, ) { super(SemanticsValueKind.DIRECT_GET, type); } toString(): string { return `[DirectGet ${this.index}]`; } forEachChild(visitor: SemanticsValueVisitor) { visitor(this.owner); super.forEachChild(visitor); } } export type MemberGetValue = | DynamicGetValue | ShapeGetValue | VTableGetValue | OffsetGetValue | OffsetGetterValue | DirectGetterValue | ElementGetValue; export type MemberSetValue = | DynamicSetValue | ShapeSetValue | VTableSetValue | OffsetSetValue | OffsetSetterValue | DirectSetterValue | ElementSetValue; export type MemberCallValue = | DynamicCallValue | ShapeCallValue | VTableCallValue | OffsetCallValue | DirectCallValue; export class NewLiteralObjectValue extends SemanticsValue { public initValues: Array; constructor(type: ObjectType) { super(SemanticsValueKind.NEW_LITERAL_OBJECT, type); this.initValues = new Array(type.meta.members.length); this.shape = type.meta.originShape; } get objectType(): ObjectType { return this.type as ObjectType; } setField(index: number, value: SemanticsValue) { this.initValues[index] = value; } } export class NewLiteralArrayValue extends SemanticsValue { constructor(type: ValueType, public initValues: SemanticsValue[]) { super(SemanticsValueKind.NEW_LITERAL_ARRAY, type); if (type instanceof ArrayType) { this.shape = (type as ArrayType).instanceType!.meta.originShape; } } } export class NewConstructorObjectValue extends SemanticsValue { constructor( type: ValueType, //public readonly clazz: SemanticsValue, public readonly parameters: SemanticsValue[], kind: SemanticsValueKind = SemanticsValueKind.NEW_CONSTRCTOR_OBJECT, ) { super(kind, type); if (type.kind == ValueTypeKind.OBJECT) this.shape = (type as ObjectType).instanceType!.meta.originShape; else if (type.kind == ValueTypeKind.TYPE_PARAMETER) { const wide_type = (type as TypeParameterType).wideType; if (wide_type.kind == ValueTypeKind.OBJECT) this.shape = ( wide_type as ObjectType ).instanceType!.meta.originShape; } } get objectType(): ObjectType { return this.type as ObjectType; } private _typeArguments?: ValueType[]; setTypeArguments(typeArgs: ValueType[]) { this._typeArguments = typeArgs; } get typeArguments(): ValueType[] | undefined { return this._typeArguments; } forEachChild(visitor: SemanticsValueVisitor) { if (this.parameters) { for (const p of this.parameters) visitor(p); } } } export class NewArrayValue extends NewConstructorObjectValue { constructor(type: ArrayType | WASMArrayType, parameters: SemanticsValue[]) { super(type, parameters, SemanticsValueKind.NEW_ARRAY); } } export class NewArrayLenValue extends SemanticsValue { constructor( type: ArrayType | WASMArrayType, public readonly len: SemanticsValue, ) { super(SemanticsValueKind.NEW_ARRAY_LEN, type); if (type instanceof ArrayType) { this.shape = type.meta.originShape; } } private _typeArguments?: ValueType[]; setTypeArguments(typeArgs: ValueType[]) { this._typeArguments = typeArgs; } get typeArguments(): ValueType[] | undefined { return this._typeArguments; } } export class NewFromClassObjectValue extends NewConstructorObjectValue { constructor( type: ValueType, public readonly clazz: SemanticsValue, parameters: SemanticsValue[], ) { super(type, parameters, SemanticsValueKind.NEW_FROM_CLASS_OBJECT); if (type.kind == ValueTypeKind.OBJECT) this.shape = (type as ObjectType).instanceType!.meta.thisShape; } } export class BlockValue extends SemanticsValue { public values: SemanticsValue[] = []; public varList?: VarDeclareNode[]; public refList?: VarDeclareNode[]; public parent?: BlockValue; constructor( type: ValueType, public readonly label: string, public readonly isLoop: boolean = false, public readonly breakTarget: boolean = false, ) { super(SemanticsValueKind.BLOCK, type); } addHead(value: SemanticsValue) { this.values.unshift(value); if (value.kind == SemanticsValueKind.BLOCK) (value as BlockValue).parent = this; } addValue(value: SemanticsValue) { this.values.push(value); if (value.kind == SemanticsValueKind.BLOCK) (value as BlockValue).parent = this; } } export class BlockBranchValue extends SemanticsValue { constructor(public readonly target: BlockValue) { super(SemanticsValueKind.BLOCK_BRANCH, Primitive.Void); } } export class BlockBranchIfValue extends SemanticsValue { constructor( public readonly target: BlockBranchValue, public condition: SemanticsValue, public readonly trueBranch: boolean = false, ) { super(SemanticsValueKind.BLOCK_BRANCH_IF, Primitive.Void); } } export class ReturnValue extends SemanticsValue { constructor(public expr: SemanticsValue | undefined) { super(SemanticsValueKind.RET, expr ? expr.type : Primitive.Void); } } export class ReBindingValue extends SemanticsValue { constructor(public contextVar: VarDeclareNode) { super(SemanticsValueKind.REBINDING, Primitive.Void); } } export class SpreadValue extends SemanticsValue { constructor(type: ValueType, public target: SemanticsValue) { super(SemanticsValueKind.SPREAD, type); } } ////////////////////////////////// export function operatorString(kind: ts.BinaryOperator): string { switch (kind) { case ts.SyntaxKind.EqualsToken: return '='; case ts.SyntaxKind.PlusEqualsToken: return '+='; case ts.SyntaxKind.MinusEqualsToken: return '-='; case ts.SyntaxKind.AsteriskAsteriskEqualsToken: return '**='; case ts.SyntaxKind.AsteriskEqualsToken: return '*='; case ts.SyntaxKind.SlashEqualsToken: return '/='; case ts.SyntaxKind.PercentEqualsToken: return '%='; case ts.SyntaxKind.AmpersandEqualsToken: return '&='; case ts.SyntaxKind.BarEqualsToken: return '|='; case ts.SyntaxKind.CaretEqualsToken: return '^='; case ts.SyntaxKind.LessThanLessThanEqualsToken: return '<<='; case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: return '>>>='; case ts.SyntaxKind.GreaterThanGreaterThanEqualsToken: return '>>='; case ts.SyntaxKind.AsteriskAsteriskToken: return '**'; case ts.SyntaxKind.AsteriskToken: return '*'; case ts.SyntaxKind.SlashToken: return '/'; case ts.SyntaxKind.PercentToken: return '%'; case ts.SyntaxKind.PlusToken: return '+'; case ts.SyntaxKind.MinusToken: return '-'; case ts.SyntaxKind.CommaToken: return ','; case ts.SyntaxKind.LessThanLessThanToken: return '<<'; case ts.SyntaxKind.GreaterThanGreaterThanToken: return '>>'; case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken: return '>>>'; case ts.SyntaxKind.LessThanToken: return '<'; case ts.SyntaxKind.LessThanEqualsToken: return '<='; case ts.SyntaxKind.GreaterThanToken: return '>'; case ts.SyntaxKind.GreaterThanEqualsToken: return '>='; case ts.SyntaxKind.InstanceOfKeyword: return 'instance of'; case ts.SyntaxKind.InKeyword: return 'in'; case ts.SyntaxKind.EqualsEqualsToken: return '=='; case ts.SyntaxKind.EqualsEqualsEqualsToken: return '==='; case ts.SyntaxKind.ExclamationEqualsEqualsToken: return '!=='; case ts.SyntaxKind.ExclamationEqualsToken: return '!='; case ts.SyntaxKind.AmpersandToken: return '&'; case ts.SyntaxKind.BarToken: return '|'; case ts.SyntaxKind.CaretToken: return '^'; case ts.SyntaxKind.AmpersandAmpersandToken: return '&&'; case ts.SyntaxKind.BarBarToken: return '||'; case ts.SyntaxKind.QuestionQuestionToken: return '??'; } return ts.SyntaxKind[kind]; } ================================================ FILE: src/semantics/value_types.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { ObjectDescription, UnknownObjectDescription } from './runtime.js'; import { DefaultTypeId, MutabilityKind, NullabilityKind, PackedTypeKind, PredefinedTypeId, } from '../utils.js'; import { BuiltinNames } from '../../lib/builtin/builtin_name.js'; export enum ValueTypeKind { PRIMITVE_BEGIN = 0, VOID, UNDEFINED, NULL, NEVER, INT, NUMBER, BOOLEAN, RAW_STRING, STRING, ANY, GENERIC, // the template generice type PRIMITVE_END, ARRAY, SET, MAP, OBJECT, INTERFACE, FUNCTION, UNION, INTERSECTION, NAMESPACE, CLOSURECONTEXT, EMPTY, TYPE_PARAMETER, // for template type parameter ENUM, TUPLE, WASM_I64, WASM_F32, WASM_ARRAY, WASM_STRUCT, } export class ValueType { constructor(public kind: ValueTypeKind, public typeId: number) {} equals(other: ValueType): boolean { if (this instanceof FunctionType) { if (this.kind != other.kind) return false; const other_func = other as unknown as FunctionType; if (!this.returnType.equals(other_func.returnType)) return false; if (this.argumentsType.length != other_func.argumentsType.length) return false; for (let i = 0; i < this.argumentsType.length; i++) { if (!this.argumentsType[i].equals(other_func.argumentsType[i])) return false; } return true; } else { return this.kind == other.kind && other.typeId == this.typeId; } } toString(): string { return `ValueType[${ValueTypeKind[this.kind]}](${this.typeId})`; } private _generic_owner?: ValueType; private _builtin_type = false; private _primitive_type = false; private _wasm_type = false; get isBuiltin(): boolean { return this._builtin_type; } setBuiltin() { this._builtin_type = true; } get isPrimitive(): boolean { return this._primitive_type; } setPrimitive() { this._primitive_type = true; } get isWASM(): boolean { return this._wasm_type; } setWASM() { this._wasm_type = true; } setGenericOwner(vt: ValueType) { this._generic_owner = vt; } get genericOwner(): ValueType | undefined { return this._generic_owner; } isSpecialized(): boolean { return this._generic_owner != undefined && this._generic_owner != null; } get genericType(): ValueType { return this._generic_owner ? this._generic_owner : this; } } export type PrimitiveValueType = | number | boolean | string | null | undefined | never; export class PrimitiveType extends ValueType { constructor(kind: ValueTypeKind, typeId: number) { super(kind, typeId); this.setPrimitive(); } toString(): string { return `${ValueTypeKind[this.kind]}(${this.typeId})`; } } export const Primitive = { Void: new PrimitiveType(ValueTypeKind.VOID, PredefinedTypeId.VOID), Null: new PrimitiveType(ValueTypeKind.NULL, PredefinedTypeId.NULL), Undefined: new PrimitiveType( ValueTypeKind.UNDEFINED, PredefinedTypeId.UNDEFINED, ), Never: new PrimitiveType(ValueTypeKind.NEVER, PredefinedTypeId.NEVER), Boolean: new PrimitiveType(ValueTypeKind.BOOLEAN, PredefinedTypeId.BOOLEAN), Int: new PrimitiveType(ValueTypeKind.INT, PredefinedTypeId.INT), Number: new PrimitiveType(ValueTypeKind.NUMBER, PredefinedTypeId.NUMBER), RawString: new PrimitiveType( ValueTypeKind.RAW_STRING, PredefinedTypeId.RAW_STRING, ), String: new PrimitiveType(ValueTypeKind.STRING, PredefinedTypeId.STRING), Any: new PrimitiveType(ValueTypeKind.ANY, PredefinedTypeId.ANY), Generic: new PrimitiveType(ValueTypeKind.GENERIC, PredefinedTypeId.GENERIC), Namespace: new PrimitiveType( ValueTypeKind.NAMESPACE, PredefinedTypeId.NAMESPACE, ), }; export class WASMType extends ValueType { constructor(kind: ValueTypeKind, typeId: number) { super(kind, typeId); this.setWASM(); } toString(): string { return `${ValueTypeKind[this.kind]}(${this.typeId})`; } } export const WASM = { I32: new WASMType(ValueTypeKind.INT, PredefinedTypeId.INT), I64: new WASMType(ValueTypeKind.WASM_I64, PredefinedTypeId.WASM_I64), F32: new WASMType(ValueTypeKind.WASM_F32, PredefinedTypeId.WASM_F32), F64: new WASMType(ValueTypeKind.NUMBER, PredefinedTypeId.NUMBER), ANYREF: new WASMType(ValueTypeKind.ANY, PredefinedTypeId.ANY), }; export class WASMArrayType extends WASMType { arrayType: ArrayType; packedTypeKind: PackedTypeKind = PackedTypeKind.Not_Packed; mutability: MutabilityKind = MutabilityKind.Mutable; nullability: NullabilityKind = NullabilityKind.Nullable; constructor( arrayType: ArrayType, packedTypeKind?: PackedTypeKind, mutability?: MutabilityKind, nullability?: NullabilityKind, ) { super(ValueTypeKind.WASM_ARRAY, PredefinedTypeId.WASM_ARRAY); this.arrayType = arrayType; if (packedTypeKind) { this.packedTypeKind = packedTypeKind; } if (mutability) { this.mutability = mutability; } if (nullability) { this.nullability = nullability; } } } export class WASMStructType extends WASMType { tupleType: TupleType; packedTypeKinds: PackedTypeKind[]; mutabilitys: MutabilityKind[]; nullability: NullabilityKind = NullabilityKind.Nullable; baseType: WASMStructType | undefined = undefined; constructor( tupleType: TupleType, packedTypeKinds?: PackedTypeKind[], mutabilitys?: MutabilityKind[], nullability?: NullabilityKind, baseType?: WASMStructType, ) { super(ValueTypeKind.WASM_STRUCT, PredefinedTypeId.WASM_STRUCT); this.tupleType = tupleType; if (packedTypeKinds) { this.packedTypeKinds = packedTypeKinds; } else { this.packedTypeKinds = new Array( this.tupleType.elements.length, ); this.packedTypeKinds.fill(PackedTypeKind.Not_Packed); } if (mutabilitys) { this.mutabilitys = mutabilitys; } else { this.mutabilitys = new Array( this.tupleType.elements.length, ); this.mutabilitys.fill(MutabilityKind.Mutable); } if (nullability) { this.nullability = nullability; } this.baseType = baseType; } } export class EmptyType extends ValueType { constructor() { super(ValueTypeKind.EMPTY, PredefinedTypeId.EMPTY); } } export class ClosureContextType extends ValueType { constructor( public parentCtxType?: ClosureContextType, public freeVarTypeList: ValueType[] = [], ) { super(ValueTypeKind.CLOSURECONTEXT, PredefinedTypeId.CLOSURECONTEXT); } } export class ValueTypeWithArguments extends ValueType { constructor(kind: ValueTypeKind, typeId: number) { super(kind, typeId); } private _typeArguments?: TypeParameterType[]; private _specialTypeArguments?: ValueType[]; get hasUninitedTypeArguments(): boolean { return ( this._typeArguments != undefined && this._typeArguments.length == 0 ); } addTypeParameter(type: TypeParameterType) { if (!this._typeArguments) this._typeArguments = []; if (this.kind == ValueTypeKind.FUNCTION) type.setOwnedByFunction(); else if ( this.kind == ValueTypeKind.OBJECT || this.kind == ValueTypeKind.ARRAY ) type.setOwnedByClass(); this._typeArguments!.push(type); } setTypeArguments(types: TypeParameterType[]) { this._typeArguments = types; } get typeArguments(): TypeParameterType[] | undefined { return this._typeArguments; } inTypeArguments(valueType: ValueType): boolean { if (!this._typeArguments) return false; return !!this._typeArguments!.find((v) => v.equals(valueType)); } setSpecialTypeArguments(types: ValueType[]) { this._specialTypeArguments = types; } get specialTypeArguments(): ValueType[] | undefined { return this._specialTypeArguments; } getSpecialTypeArg(type: TypeParameterType) { let typeIdx = -1; for (let i = 0; i < this.typeArguments!.length; i++) { if (this.typeArguments![i].name === type.name) { typeIdx = i; } } if (this.specialTypeArguments) { return this.specialTypeArguments![typeIdx]; } return undefined; } getSpecialTypeArgs(types: TypeParameterType[]) { const specialTypeArgs: ValueType[] = []; for (const type of types) { specialTypeArgs.push(this.getSpecialTypeArg(type)!); } return specialTypeArgs; } } export class SetType extends ValueTypeWithArguments { constructor( typeId: number, public element: ValueType, public meta?: ObjectDescription, ) { super(ValueTypeKind.SET, typeId); } equals(other: ValueType): boolean { if (!super.equals(other)) return false; return ( this.kind == other.kind && this.element.equals((other as SetType).element) ); } toString(): string { return `Set<${this.element.toString()}>(${this.typeId})`; } } export class MapType extends ValueTypeWithArguments { constructor( typeId: number, public key: ValueType, public value: ValueType, public meta?: ObjectDescription, ) { super(ValueTypeKind.MAP, typeId); } equals(other: ValueType): boolean { if (!super.equals(other)) return false; return ( this.kind == other.kind && this.key.equals((other as MapType).key) && this.value.equals((other as MapType).value) ); } toString(): string { return `Map<${this.key.toString()}, ${this.value.toString()}>(${ this.typeId })`; } } export enum ObjectTypeFlag { OBJECT = 0, // normal object LITERAL, CLASS, UNION, } export class ObjectType extends ValueTypeWithArguments { public super?: ObjectType; public impl?: ObjectType; // not undefined iff it implement an interface private _class_or_instance?: ObjectType; private _numberIndexType?: ValueType; private _stringIndexType?: ValueType; private _implId = DefaultTypeId; constructor( typeId: number, public readonly meta: ObjectDescription, public readonly flags: number = 0, kind: ValueTypeKind = ValueTypeKind.OBJECT, ) { super(kind, typeId); } public isClassObject(): boolean { return this.flags == ObjectTypeFlag.CLASS; } public isLiteralObject(): boolean { return this.flags == ObjectTypeFlag.LITERAL; } public isObject(): boolean { return this.flags == ObjectTypeFlag.OBJECT; } public get classType(): ObjectType | undefined { if (this.isClassObject()) return this; if (this.isObject()) return this._class_or_instance; if (this.isLiteralObject()) return this.instanceType; return undefined; } public set classType(c: ObjectType | undefined) { if (this.isObject()) this._class_or_instance = c; } public get instanceType(): ObjectType | undefined { return this.isClassObject() ? this._class_or_instance : this; } public set instanceType(inst: ObjectType | undefined) { if (this.isClassObject()) this._class_or_instance = inst; } public get numberIndexType(): ValueType | undefined { return this._numberIndexType; } public setNumberIndexType(type: ValueType) { this._numberIndexType = type; } public get stringIndexType(): ValueType | undefined { return this._stringIndexType; } public setStringIndexType(type: ValueType) { this._stringIndexType = type; } public get implId() { return this._implId; } public set implId(id: number) { this._implId = id; } public clone( typeId: number, meta: ObjectDescription, flags: number, implId: number, ): ObjectType { const res = new ObjectType(typeId, meta, flags); res.implId = implId; return res; } equals(other: ValueType): boolean { //if (!super.equals(other)) return false; if (this.kind != other.kind) return false; const other_type = other as ObjectType; // if it is a comparison of two objectLiteral types, only need to determine whether their typeIds are the same. if ( this.flags == ObjectTypeFlag.LITERAL && other_type.flags == ObjectTypeFlag.LITERAL ) { if (this.typeId == other_type.typeId) return true; else return false; } if (this.meta === other_type.meta) return true; return false; } toString(): string { return `ObjectType[${this.meta.name}:${ObjectTypeFlag[this.flags]}](${ this.typeId })`; } } export class ArrayType extends ObjectType { constructor(typeId: number, meta: ObjectDescription, flags: number) { super(typeId, meta, flags, ValueTypeKind.ARRAY); this._element = Primitive.Undefined; } private _element: ValueType; setElement(element: ValueType) { this._element = element; } get element(): ValueType { if (this.numberIndexType) return this.numberIndexType; return this._element; } public get numberIndexType(): ValueType | undefined { if (super.numberIndexType) return super.numberIndexType; if (this.specialTypeArguments) return this.specialTypeArguments[0]; if (this.isObject()) return this.typeArguments ? this.typeArguments[0] : undefined; return undefined; } equals(other: ValueType): boolean { return ( this.kind == other.kind && this.element.equals((other as ArrayType).element) ); } public clone( typeId: number, meta: ObjectDescription, flags: number, ): ObjectType { return new ArrayType(typeId, meta, flags); } toString(): string { return `Array<${this.element.toString()}(${ this.isClassObject() ? 'CLASS' : 'OBJECT' })>(${this.typeId})`; } } export const UnknownObjectType = new ObjectType( DefaultTypeId, UnknownObjectDescription, ); export class UnionType extends ValueType { constructor( typeId: number, public types: Set, public wideType: ValueType, ) { super(ValueTypeKind.UNION, typeId); } toString(): string { const ts: string[] = []; for (const t of this.types) { ts.push(t.toString()); } return `[UNION{${this.wideType.toString()}} ${ts.join('|')}](${ this.typeId })`; } equals(other: ValueType): boolean { if (!super.equals(other)) return false; if (this.kind != other.kind) return false; const other_union = other as UnionType; if (this.types.size != other_union.types.size) return false; for (const t of this.types) { let matched = other_union.types.has(t); if (!matched) { for (const o of other_union.types) { if (o.equals(t)) { matched = true; break; } } } if (!matched) return false; } return true; } } export class FunctionType extends ValueTypeWithArguments { constructor( typeId: number, public returnType: ValueType, public argumentsType: ValueType[], public isOptionalParams: boolean[] = [], public restParamIdx = -1, public envParamLen = BuiltinNames.envParamLen, ) { super(ValueTypeKind.FUNCTION, typeId); } getRestParam(): ValueType | undefined { if (this.restParamIdx < 0) return undefined; if (this.argumentsType.length <= 0) return Primitive.Any; const last_type = this.argumentsType[this.restParamIdx]; if (last_type.kind == ValueTypeKind.ARRAY) { return (last_type as ArrayType).element; } return last_type; } hasParamType() { return this.restParamIdx >= 0; } getParamType(idx: number): ValueType | undefined { if ( (this.hasParamType() && idx < this.restParamIdx) || !this.hasParamType() ) { return this.argumentsType[idx]; } return this.getRestParam(); } equals(other: ValueType): boolean { if (this.kind != other.kind) return false; const other_func = other as FunctionType; if (!this.returnType.equals(other_func.returnType)) return false; if (this.argumentsType.length != other_func.argumentsType.length) return false; for (let i = 0; i < this.argumentsType.length; i++) { if (!this.argumentsType[i].equals(other_func.argumentsType[i])) return false; } return true; } toString(): string { const params: string[] = []; for (const p of this.argumentsType) { params.push(p.toString()); } return `Function(${params.join(',')}) : ${this.returnType.toString()}`; } } enum TypeParameterOwnerType { FUNCTION, CLASS, CLOSURE, } export class TypeParameterType extends ValueType { private _ownerType: TypeParameterOwnerType = TypeParameterOwnerType.FUNCTION; private _specialTypeArgument?: ValueType; constructor( typeId: number, public readonly name: string, public readonly wideType: ValueType, public readonly index: number, public readonly defaultType?: ValueType, ) { super(ValueTypeKind.TYPE_PARAMETER, typeId); } setSpecialTypeArgument(type: ValueType) { this._specialTypeArgument = type; } get specialTypeArgument(): ValueType | undefined { return this._specialTypeArgument; } setOwnedByFunction() { this._ownerType = TypeParameterOwnerType.FUNCTION; } get ownedByFunction(): boolean { return this._ownerType == TypeParameterOwnerType.FUNCTION; } setOwnedByClass() { this._ownerType = TypeParameterOwnerType.CLASS; } get ownedByClass(): boolean { return this._ownerType == TypeParameterOwnerType.CLASS; } setOwnedByClosure() { this._ownerType = TypeParameterOwnerType.CLOSURE; } get ownedByClosure(): boolean { return this._ownerType == TypeParameterOwnerType.CLOSURE; } toString(): string { return `TypeParameter(${this.name}@${this.index} ${this.typeId}) Wide:${this.wideType} Default: ${this.defaultType}`; } equals(other: ValueType) { if (other.kind != ValueTypeKind.TYPE_PARAMETER) return false; if (this.typeId != -1 && other.typeId != -1) { return this.typeId == other.typeId; } if (!this.wideType.equals((other as TypeParameterType).wideType)) return false; return true; } } export class EnumType extends ValueType { constructor( typeId: number, public name: string, // global name public memberType: ValueType, public members: Map, ) { super(ValueTypeKind.ENUM, typeId); } toString(): string { let i = 0; let s = ''; this.members.forEach((v, k) => { if (i < 4) { s += `${k}=${v},`; i++; } else if (i == 4) { s = s + '...'; } }); return `EnumType[${this.name}](${s})`; } } export class TupleType extends ValueType { constructor(typeId: number, public elements: ValueType[]) { super(ValueTypeKind.TUPLE, typeId); } toString(): string { const ts: string[] = []; for (const t of this.elements) { ts.push(t.toString()); } return `[TUPLE{${this.elements.join(',')}}}](${this.typeId})`; } } ================================================ FILE: src/statement.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import ts from 'typescript'; import { assert } from 'console'; import { ParserContext } from './frontend.js'; import { BinaryExpression, CallExpression, ElementAccessExpression, Expression, IdentifierExpression, NumberLiteralExpression, PropertyAccessExpression, UnaryExpression, UndefinedKeywordExpression, SuperExpression, ArrayLiteralExpression, StringLiteralExpression, EnumerateKeysExpression, } from './expression.js'; import { Scope, ScopeKind, FunctionScope, BlockScope } from './scope.js'; import { parentIsFunctionLike, Stack, getModulePath, getGlobalScopeByModuleName, SourceLocation, addSourceMapLoc, getCurScope, processGenericType, } from './utils.js'; import { ModifierKind, Variable, Parameter } from './variable.js'; import { TSArray, TSClass, Type, TypeKind, builtinTypes, TSTypeParameter, } from './type.js'; import { Logger } from './log.js'; import { StatementError, UnimplementError } from './error.js'; import { getConfig } from '../config/config_mgr.js'; type StatementKind = ts.SyntaxKind; export class Statement { private _scope: Scope | null = null; debugLoc: SourceLocation | null = null; public tsNode?: ts.Node; constructor(private kind: StatementKind) {} get statementKind(): StatementKind { return this.kind; } setScope(scope: Scope) { this._scope = scope; } getScope(): Scope | null { return this._scope; } clone() { const stmt = new Statement(this.statementKind); return stmt; } } /** in order to keep order of namespace in parent level scope, creat a corresponding statement * for namespace */ export class ModDeclStatement extends Statement { constructor(public scope: Scope) { super(ts.SyntaxKind.ModuleDeclaration); } clone(): ModDeclStatement { const stmt = new ModDeclStatement(this.scope); return stmt; } } export class IfStatement extends Statement { constructor( private condition: Expression, private ifTrue: Statement, private ifFalse: Statement | null, ) { super(ts.SyntaxKind.IfStatement); } get ifCondition(): Expression { return this.condition; } get ifIfTrue(): Statement { return this.ifTrue; } get ifIfFalse(): Statement | null { return this.ifFalse; } clone(): IfStatement { const stmt = new IfStatement( this.ifCondition, this.ifIfTrue, this.ifIfFalse, ); return stmt; } } export class BlockStatement extends Statement { constructor() { super(ts.SyntaxKind.Block); } clone(): BlockStatement { const stmt = new BlockStatement(); const scope = this.getScope(); if (scope !== null) stmt.setScope(scope); return stmt; } } export class ReturnStatement extends Statement { constructor(private expr: Expression | null) { super(ts.SyntaxKind.ReturnStatement); } get returnExpression(): Expression | null { return this.expr; } clone(): ReturnStatement { const stmt = new ReturnStatement(this.returnExpression); return stmt; } } // create 'while' or 'do...while' loop export class BaseLoopStatement extends Statement { constructor( kind: StatementKind, private _loopLabel: string, private _blockLabel: string, private _continueLabel: string | null, private cond: Expression, private body: Statement, ) { super(kind); } get loopLabel(): string { return this._loopLabel; } get loopBlockLabel(): string { return this._blockLabel; } get loopContinueLable(): string | null { return this._continueLabel; } get loopCondtion(): Expression { return this.cond; } get loopBody(): Statement { return this.body; } clone(): BaseLoopStatement { const stmt = new BaseLoopStatement( this.statementKind, this.loopLabel, this.loopBlockLabel, this.loopContinueLable, this.loopCondtion, this.loopBody, ); return stmt; } } export class ForStatement extends Statement { constructor( private label: string, private blockLabel: string, private continueLabel: string | null, private cond: Expression | null, private body: Statement, /** VariableStatement or ExpressionStatement */ private initializer: Statement | null, private incrementor: Expression | null, ) { super(ts.SyntaxKind.ForStatement); } get forLoopLabel(): string { return this.label; } get forLoopBlockLabel(): string { return this.blockLabel; } get forContinueLable(): string | null { return this.continueLabel; } get forLoopCondtion(): Expression | null { return this.cond; } get forLoopBody(): Statement { return this.body; } get forLoopInitializer(): Statement | null { return this.initializer; } get forLoopIncrementor(): Expression | null { return this.incrementor; } clone(): ForStatement { const stmt = new ForStatement( this.forLoopLabel, this.forLoopBlockLabel, this.forContinueLable, this.forLoopCondtion, this.forLoopBody, this.forLoopInitializer, this.forLoopIncrementor, ); return stmt; } } export class ExpressionStatement extends Statement { constructor(private expr: Expression) { super(ts.SyntaxKind.ExpressionStatement); } get expression(): Expression { return this.expr; } clone(): ExpressionStatement { const stmt = new ExpressionStatement(this.expression); return stmt; } } export class EmptyStatement extends Statement { constructor() { super(ts.SyntaxKind.EmptyStatement); } clone(): EmptyStatement { const stmt = new EmptyStatement(); return stmt; } } export class CaseClause extends Statement { constructor(private expr: Expression, private statements: Statement[]) { super(ts.SyntaxKind.CaseClause); } get caseExpr(): Expression { return this.expr; } get caseStatements(): Statement[] { return this.statements; } clone(): CaseClause { const stmt = new CaseClause(this.caseExpr, this.caseStatements); return stmt; } } export class DefaultClause extends Statement { constructor(private statements: Statement[]) { super(ts.SyntaxKind.DefaultClause); } get caseStatements(): Statement[] { return this.statements; } clone(): DefaultClause { const stmt = new DefaultClause(this.caseStatements); return stmt; } } export class CaseBlock extends Statement { constructor( private _switchLabel: string, private _breakLabel: string, private causes: Statement[], ) { super(ts.SyntaxKind.CaseBlock); } get switchLabel(): string { return this._switchLabel; } get breakLabel(): string { return this._breakLabel; } get caseCauses(): Statement[] { return this.causes; } clone(): CaseBlock { const stmt = new CaseBlock( this.switchLabel, this.breakLabel, this.caseCauses, ); return stmt; } } export class SwitchStatement extends Statement { constructor(private cond: Expression, private caseBlock: Statement) { super(ts.SyntaxKind.SwitchStatement); } get switchCondition(): Expression { return this.cond; } get switchCaseBlock(): Statement { return this.caseBlock; } clone(): SwitchStatement { const stmt = new SwitchStatement( this.switchCondition, this.switchCaseBlock, ); return stmt; } } export class BreakStatement extends Statement { constructor(private label: string) { super(ts.SyntaxKind.BreakStatement); } get breakLabel(): string { return this.label; } clone(): BreakStatement { const stmt = new BreakStatement(this.breakLabel); return stmt; } } export class ContinueStatement extends Statement { constructor(private label: string) { super(ts.SyntaxKind.ContinueStatement); } get continueLabel(): string { return this.label; } clone(): ContinueStatement { const stmt = new ContinueStatement(this.continueLabel); return stmt; } } export class FunctionDeclarationStatement extends Statement { public tmpVar?: Variable; constructor(private _funcScope: FunctionScope) { super(ts.SyntaxKind.FunctionDeclaration); } get funcScope(): FunctionScope { return this._funcScope; } clone(): FunctionDeclarationStatement { const stmt = new FunctionDeclarationStatement(this.funcScope); return stmt; } } export class VariableStatement extends Statement { private variableArray: Variable[] = []; constructor() { super(ts.SyntaxKind.VariableStatement); } addVariable(variable: Variable) { this.variableArray.push(variable); } get varArray(): Variable[] { return this.variableArray; } clone(): VariableStatement { const stmt = new VariableStatement(); this.varArray.forEach((v) => { stmt.addVariable(v); }); return stmt; } } export class ImportDeclaration extends Statement { importModuleStartFuncName = ''; constructor() { super(ts.SyntaxKind.ImportDeclaration); } clone(): ImportDeclaration { const stmt = new ImportDeclaration(); return stmt; } } export class ThrowStatement extends Statement { expr: Expression; constructor(expr: Expression) { super(ts.SyntaxKind.ThrowStatement); this.expr = expr; } clone(): ThrowStatement { const stmt = new ThrowStatement(this.expr); return stmt; } } export class CatchClauseStatement extends Statement { catchBlockStmt: BlockStatement; catchVar: IdentifierExpression | undefined = undefined; constructor(catchBlock: BlockStatement) { super(ts.SyntaxKind.CatchClause); this.catchBlockStmt = catchBlock; } clone(): CatchClauseStatement { const stmt = new CatchClauseStatement(this.catchBlockStmt); stmt.catchVar = this.catchVar; return stmt; } } export class TryStatement extends Statement { label: string; tryBlockStmt: BlockStatement; catchClauseStmt: CatchClauseStatement | undefined = undefined; finallyBlockStmt: BlockStatement | undefined = undefined; constructor(tryLable: string, tryBlock: BlockStatement) { super(ts.SyntaxKind.TryStatement); this.label = tryLable; this.tryBlockStmt = tryBlock; } clone(): TryStatement { const stmt = new TryStatement(this.label, this.tryBlockStmt); stmt.catchClauseStmt = this.catchClauseStmt; stmt.finallyBlockStmt = this.finallyBlockStmt; return stmt; } } export class StatementProcessor { private loopLabelStack = new Stack(); private breakLabelsStack = new Stack(); // mark if continue statement in a loop private continueFlagMap = new Set(); private switchLabelStack = new Stack(); private tryLabelStack = new Stack(); private currentScope: Scope | null = null; private emitSourceMap = false; constructor(private parserCtx: ParserContext) {} visit() { this.emitSourceMap = getConfig().sourceMap; this.parserCtx.nodeScopeMap.forEach((scope, node) => { this.parserCtx.currentScope = scope; this.currentScope = scope; /** arrow function body is a ts.expression */ if (ts.isArrowFunction(node) && !ts.isBlock(node.body)) { const expr = this.parserCtx.expressionProcessor.visitNode( node.body, ); const returnType = (scope as FunctionScope).funcType.returnType; const stmt = returnType.kind === TypeKind.VOID ? new ExpressionStatement(expr) : new ReturnStatement(expr); scope.addStatement(stmt); } /* During the traverse, it will enter the inner block scope, so we skip BlockScope here */ if ( scope.kind !== ScopeKind.BlockScope && scope.kind !== ScopeKind.ClassScope ) { ts.forEachChild(node, (node) => { const stmt = this.visitNode(node); if (stmt) { scope.addStatement(stmt); } }); } }); } visitNode(node: ts.Node): Statement | null { const stm = this.visitNodeInternal(node); if (stm != null) stm.tsNode = node; return stm; } visitNodeInternal(node: ts.Node): Statement | null { switch (node.kind) { case ts.SyntaxKind.ExportDeclaration: case ts.SyntaxKind.ImportDeclaration: { const importDeclaration = < ts.ImportDeclaration | ts.ExportDeclaration >node; // Get the import module name according to the relative position of current scope const importModuleName = getModulePath( importDeclaration, this.currentScope!.getRootGloablScope()!, ); if (importModuleName === undefined) return null; const importModuleScope = getGlobalScopeByModuleName( importModuleName!, this.parserCtx.globalScopes, ); const importStmt = new ImportDeclaration(); if (!importModuleScope.isCircularImport) { const currentGlobalScope = this.currentScope!.getRootGloablScope()!; currentGlobalScope.importStartFuncNameList.push( importModuleScope.startFuncName, ); importModuleScope.isCircularImport = true; importStmt.importModuleStartFuncName = importModuleScope.startFuncName; return importStmt; } /** Currently, we put all ts files into a whole wasm file. * So we don't need to collect import information here. * If we generate several wasm files, we need to collect here. */ return importStmt; } case ts.SyntaxKind.VariableStatement: { const varStatementNode = node; const varStatement = new VariableStatement(); const varDeclarationList = varStatementNode.declarationList; this.currentScope = this.parserCtx.getScopeByNode(node)!; this.addVariableInVarStmt( varDeclarationList, varStatement, this.currentScope, ); return varStatement; } case ts.SyntaxKind.IfStatement: { const ifStatementNode = node; const condtion: Expression = this.parserCtx.expressionProcessor.visitNode( ifStatementNode.expression, ); const ifTrue: Statement = this.visitNode( ifStatementNode.thenStatement, )!; const ifFalse: Statement | null = ifStatementNode.elseStatement ? this.visitNode(ifStatementNode.elseStatement) : null; const ifStmt = new IfStatement(condtion, ifTrue, ifFalse); if (this.emitSourceMap) { addSourceMapLoc(ifStmt, node); } return ifStmt; } case ts.SyntaxKind.Block: { /* every ts.Block(except function.body) has a corresponding block scope and BlockStatement */ const blockNode = node; const scope = this.parserCtx.getScopeByNode(blockNode)!; for (const stmt of blockNode.statements) { const compiledStmt = this.visitNode(stmt)!; if (!compiledStmt) { continue; } if ( compiledStmt instanceof ExpressionStatement && compiledStmt.expression instanceof SuperExpression && scope.statements.length > 0 // must be the first statement ) { scope.statements.unshift(compiledStmt); } else { scope.addStatement(compiledStmt); } } /* Block of function scope, just add statements to parent scope, don't create a new BlockStatement */ if (parentIsFunctionLike(node)) { return null; } const block = new BlockStatement(); if (this.emitSourceMap) { addSourceMapLoc(block, node); } block.setScope(scope); return block; } case ts.SyntaxKind.ReturnStatement: { const returnStatementNode = node; const retStmt = new ReturnStatement( returnStatementNode.expression ? this.parserCtx.expressionProcessor.visitNode( returnStatementNode.expression, ) : null, ); if (this.emitSourceMap) { addSourceMapLoc(retStmt, node); } return retStmt; } case ts.SyntaxKind.WhileStatement: { const whileStatementNode = node; this.currentScope = this.parserCtx.getScopeByNode(node)!; const scope = this.currentScope; const loopLabel = 'while_loop_' + this.loopLabelStack.size(); const breakLabels = this.breakLabelsStack; breakLabels.push(loopLabel + 'block'); const blockLabel = breakLabels.peek(); const continueLabel = this.getLoopContinueLabel(loopLabel); this.loopLabelStack.push(loopLabel); const expr = this.parserCtx.expressionProcessor.visitNode( whileStatementNode.expression, ); const statement = this.visitNode(whileStatementNode.statement)!; this.breakLabelsStack.pop(); this.loopLabelStack.pop(); const loopStatment = new BaseLoopStatement( ts.SyntaxKind.WhileStatement, loopLabel, blockLabel, this.continueFlagMap.has(continueLabel) ? continueLabel : null, expr, statement, ); if (this.emitSourceMap) { addSourceMapLoc(loopStatment, node); } loopStatment.setScope(scope); /* current scope is outter block scope */ scope.addStatement(loopStatment); const block = new BlockStatement(); if (this.emitSourceMap) { addSourceMapLoc(block, node); } block.setScope(scope); return block; } case ts.SyntaxKind.DoStatement: { const doWhileStatementNode = node; this.currentScope = this.parserCtx.getScopeByNode(node)!; const scope = this.currentScope; const loopLabel = 'do_loop_' + this.loopLabelStack.size(); const breakLabels = this.breakLabelsStack; breakLabels.push(loopLabel + 'block'); const blockLabel = breakLabels.peek(); const continueLabel = this.getLoopContinueLabel(loopLabel); this.loopLabelStack.push(loopLabel); const expr = this.parserCtx.expressionProcessor.visitNode( doWhileStatementNode.expression, ); const statement = this.visitNode( doWhileStatementNode.statement, )!; this.breakLabelsStack.pop(); this.loopLabelStack.pop(); const loopStatment = new BaseLoopStatement( ts.SyntaxKind.DoStatement, loopLabel, blockLabel, this.continueFlagMap.has(continueLabel) ? continueLabel : null, expr, statement, ); if (this.emitSourceMap) { addSourceMapLoc(loopStatment, node); } loopStatment.setScope(scope); /* current scope is outter block scope */ scope.addStatement(loopStatment); const block = new BlockStatement(); if (this.emitSourceMap) { addSourceMapLoc(block, node); } block.setScope(scope); return block; } case ts.SyntaxKind.ForStatement: { const forStatementNode = node; this.currentScope = this.parserCtx.getScopeByNode(node)!; const scope = this.currentScope; const loopLabel = 'for_loop_' + this.loopLabelStack.size(); const breakLabels = this.breakLabelsStack; breakLabels.push(loopLabel + 'block'); const blockLabel = breakLabels.peek(); const continueLabel = this.getLoopContinueLabel(loopLabel); this.loopLabelStack.push(loopLabel); let initializer = null; if (forStatementNode.initializer) { const forInit = forStatementNode.initializer; let initStmt: Statement; if (ts.isVariableDeclarationList(forInit)) { initStmt = new VariableStatement(); this.addVariableInVarStmt( forInit, initStmt as VariableStatement, scope, true, ); } else { initStmt = new ExpressionStatement( this.parserCtx.expressionProcessor.visitNode( forInit, ), ); } initializer = initStmt; } const cond = forStatementNode.condition ? this.parserCtx.expressionProcessor.visitNode( forStatementNode.condition, ) : null; const incrementor = forStatementNode.incrementor ? this.parserCtx.expressionProcessor.visitNode( forStatementNode.incrementor, ) : null; const statement = this.visitNode(forStatementNode.statement)!; this.breakLabelsStack.pop(); this.loopLabelStack.pop(); const forStatement = new ForStatement( loopLabel, blockLabel, this.continueFlagMap.has(continueLabel) ? continueLabel : null, cond, statement, initializer, incrementor, ); if (this.emitSourceMap) { addSourceMapLoc(forStatement, node); } forStatement.setScope(scope); /* current scope is outter block scope */ scope.addStatement(forStatement); const block = new BlockStatement(); if (this.emitSourceMap) { addSourceMapLoc(block, node); } block.setScope(scope); return block; } case ts.SyntaxKind.ForOfStatement: { const forOfStmtNode = node; this.currentScope = this.parserCtx.getScopeByNode(node)!; const scope = this.currentScope; const loopLabel = 'for_loop_' + this.loopLabelStack.size(); const breakLabels = this.breakLabelsStack; breakLabels.push(loopLabel + 'block'); const blockLabel = breakLabels.peek(); const continueLabel = this.getLoopContinueLabel(loopLabel); this.loopLabelStack.push(loopLabel); const forStatement = this.convertForOfToForLoop( forOfStmtNode, scope, loopLabel, blockLabel, continueLabel, ); this.breakLabelsStack.pop(); this.loopLabelStack.pop(); forStatement.setScope(scope); scope.addStatement(forStatement); const block = new BlockStatement(); if (this.emitSourceMap) { addSourceMapLoc(block, node); } block.setScope(scope); return block; } case ts.SyntaxKind.ForInStatement: { const forInStmtNode = node; this.currentScope = this.parserCtx.getScopeByNode(node)!; const scope = this.currentScope; const loopLabel = 'for_loop_' + this.loopLabelStack.size(); const breakLabels = this.breakLabelsStack; breakLabels.push(loopLabel + 'block'); const blockLabel = breakLabels.peek(); const continueLabel = this.getLoopContinueLabel(loopLabel); this.loopLabelStack.push(loopLabel); const forStatement = this.convertForInToForLoop( forInStmtNode, scope, loopLabel, blockLabel, continueLabel, ); this.breakLabelsStack.pop(); this.loopLabelStack.pop(); forStatement.setScope(scope); scope.addStatement(forStatement); const block = new BlockStatement(); if (this.emitSourceMap) { addSourceMapLoc(block, node); } block.setScope(scope); return block; } case ts.SyntaxKind.ExpressionStatement: { const exprStatement = node; const exprStmt = new ExpressionStatement( this.parserCtx.expressionProcessor.visitNode( exprStatement.expression, ), ); if (this.emitSourceMap) { addSourceMapLoc(exprStmt, node); } return exprStmt; } case ts.SyntaxKind.EmptyStatement: { const emptyStmt = new EmptyStatement(); if (this.emitSourceMap) { addSourceMapLoc(emptyStmt, node); } return emptyStmt; } case ts.SyntaxKind.SwitchStatement: { const switchStatementNode = node; const switchLabels = this.switchLabelStack; switchLabels.push(switchLabels.size()); const breakLabels = this.breakLabelsStack; breakLabels.push('break-switch-' + switchLabels.size()); const expr = this.parserCtx.expressionProcessor.visitNode( switchStatementNode.expression, ); const caseBlock = this.visitNode( switchStatementNode.caseBlock, )!; switchLabels.pop(); breakLabels.pop(); const swicthStmt = new SwitchStatement(expr, caseBlock); if (this.emitSourceMap) { addSourceMapLoc(swicthStmt, node); } return swicthStmt; } case ts.SyntaxKind.CaseBlock: { const caseBlockNode = node; this.currentScope = this.parserCtx.getScopeByNode(node)!; const scope = this.currentScope; const breakLabelsStack = this.breakLabelsStack; const switchLabels = this.switchLabelStack; const switchLabel = '_' + switchLabels.peek().toString(); const clauses = new Array(); for (let i = 0; i !== caseBlockNode.clauses.length; ++i) { clauses.push(this.visitNode(caseBlockNode.clauses[i])!); } const caseBlock = new CaseBlock( switchLabel, breakLabelsStack.peek(), clauses, ); if (this.emitSourceMap) { addSourceMapLoc(caseBlock, node); } caseBlock.setScope(scope); return caseBlock; } case ts.SyntaxKind.CaseClause: { const caseClauseNode = node; this.currentScope = this.parserCtx.getScopeByNode(node)!; const scope = this.currentScope; const expr = this.parserCtx.expressionProcessor.visitNode( caseClauseNode.expression, ); const statements = new Array(); const caseStatements = caseClauseNode.statements; for (let i = 0; i != caseStatements.length; ++i) { statements.push(this.visitNode(caseStatements[i])!); } const caseCause = new CaseClause(expr, statements); if (this.emitSourceMap) { addSourceMapLoc(caseCause, node); } caseCause.setScope(scope); return caseCause; } case ts.SyntaxKind.DefaultClause: { const defaultClauseNode = node; this.currentScope = this.parserCtx.getScopeByNode(node)!; const scope = this.currentScope; const statements = new Array(); const caseStatements = defaultClauseNode.statements; for (let i = 0; i != caseStatements.length; ++i) { statements.push(this.visitNode(caseStatements[i])!); } const defaultClause = new DefaultClause(statements); if (this.emitSourceMap) { addSourceMapLoc(defaultClause, node); } defaultClause.setScope(scope); return defaultClause; } case ts.SyntaxKind.BreakStatement: { const breakStatementNode = node; assert(!breakStatementNode.label, 'not support goto'); const breakStmt = new BreakStatement( this.breakLabelsStack.peek(), ); if (this.emitSourceMap) { addSourceMapLoc(breakStmt, node); } return breakStmt; } case ts.SyntaxKind.ContinueStatement: { const label = this.getLoopContinueLabel( this.loopLabelStack.peek(), ); this.continueFlagMap.add(label); const continueStmt = new ContinueStatement(label); if (this.emitSourceMap) { addSourceMapLoc(continueStmt, node); } return continueStmt; } case ts.SyntaxKind.FunctionDeclaration: { const funcScope = getCurScope( node, this.parserCtx.nodeScopeMap, ) as FunctionScope; const funcDeclStmt = new FunctionDeclarationStatement( funcScope, ); const rootFuncScope = funcScope.getRootFunctionScope(); if (rootFuncScope && rootFuncScope !== funcScope) { const tmpVar = new Variable( funcScope.getName(), funcScope.funcType, [ModifierKind.const], -1, false, this.parserCtx.expressionProcessor.visitNode(node), ); funcScope.parent!.addVariable(tmpVar); funcDeclStmt.tmpVar = tmpVar; } return funcDeclStmt; } case ts.SyntaxKind.ModuleDeclaration: { const md = node; const moduleBlock = md.body!; const scope = this.parserCtx.nodeScopeMap.get(moduleBlock); if (!scope) { throw new StatementError( `failed to find scope for ModuleDeclaration ${md.name}`, ); } return new ModDeclStatement(scope); } case ts.SyntaxKind.ThrowStatement: { const throwStmtNode = node; const expr = this.parserCtx.expressionProcessor.visitNode( throwStmtNode.expression, ); const throwStmt = new ThrowStatement(expr); return throwStmt; } case ts.SyntaxKind.CatchClause: { const catchClauseNode = node; const catchBlockStmt = this.visitNode(catchClauseNode.block)!; const catchClauseStmt = new CatchClauseStatement( catchBlockStmt, ); if (catchClauseNode.variableDeclaration) { const varDecNode = ( catchClauseNode.variableDeclaration ); const catchVarName = varDecNode.getText(); catchClauseStmt.catchVar = new IdentifierExpression( catchVarName, ); } return catchClauseStmt; } case ts.SyntaxKind.TryStatement: { const tryNode = node; const trySize = this.tryLabelStack.size(); this.tryLabelStack.push(trySize); const tryBlockStmt = this.visitNode(tryNode.tryBlock)!; const tryLable = 'try_' + trySize; const tryStmt = new TryStatement(tryLable, tryBlockStmt); if (tryNode.catchClause) { const catchClauseStmt = this.visitNode( tryNode.catchClause, )! as CatchClauseStatement; tryStmt.catchClauseStmt = catchClauseStmt; } if (tryNode.finallyBlock) { const finallyBlockStmt = this.visitNode( tryNode.finallyBlock, )! as BlockStatement; tryStmt.finallyBlockStmt = finallyBlockStmt; } return tryStmt; } default: Logger.info( `Encounter unprocessed statements, kind: [${ ts.SyntaxKind[node.kind] }]`, ); break; } return null; } addVariableInVarStmt( varDeclarationList: ts.VariableDeclarationList, varStatement: VariableStatement, currentScope: Scope, isDefinedInInitializer = false, ) { for (const varDeclaration of varDeclarationList.declarations) { const varDecNode = varDeclaration; const varName = (varDecNode.name).getText()!; const variable = this.currentScope!.findVariable(varName); if (!variable) { throw new StatementError( 'can not find ' + varName + ' in current scope', ); } variable.needReBinding = isDefinedInInitializer && !variable.isFuncScopedVar(); varStatement.addVariable(variable); if (variable.isFuncScopedVar() && varDecNode.initializer) { const identifierExpr = new IdentifierExpression(varName); identifierExpr.setExprType(variable.varType); const initExpr = this.parserCtx.expressionProcessor.visitNode( varDecNode.initializer, ); const assignExpr = new BinaryExpression( ts.SyntaxKind.EqualsToken, identifierExpr, initExpr, ); assignExpr.setExprType(variable.varType); const expressionStmt = new ExpressionStatement(assignExpr); if (this.emitSourceMap) { addSourceMapLoc(expressionStmt, varDecNode); addSourceMapLoc(initExpr, varDecNode.initializer); } currentScope.addStatement(expressionStmt); } } } createFieldAssignStmt( initializer: ts.Node, classType: TSClass, fieldType: Type, fieldName: string, ): Statement { const thisExpr = new IdentifierExpression('this'); thisExpr.setExprType(classType); const fieldExpr = new IdentifierExpression(fieldName); fieldExpr.setExprType(fieldType); const propAccessExpr = new PropertyAccessExpression( thisExpr, fieldExpr, ); propAccessExpr.setExprType(fieldType); const initExpr = this.parserCtx.expressionProcessor.visitNode(initializer); const assignExpr = new BinaryExpression( ts.SyntaxKind.EqualsToken, propAccessExpr, initExpr, ); assignExpr.setExprType(fieldType); return new ExpressionStatement(assignExpr); } private convertForOfToForLoop( forOfStmtNode: ts.ForOfStatement, scope: Scope, loopLabel: string, blockLabel: string, continueLabel: string, ): Statement { let elementExpr: IdentifierExpression; const forOfInitializer = forOfStmtNode.initializer; if (ts.isVariableDeclarationList(forOfInitializer)) { elementExpr = this.parserCtx.expressionProcessor.visitNode( forOfInitializer.declarations[0].name, ) as IdentifierExpression; } else { // ts.Identifier elementExpr = this.parserCtx.expressionProcessor.visitNode( forOfInitializer, ) as IdentifierExpression; } const expr = this.parserCtx.expressionProcessor.visitNode( forOfStmtNode.expression, ); const isStaticExpr = expr.exprType.kind === TypeKind.STRING || expr.exprType.kind === TypeKind.ARRAY; const loopIndexLabel = `@loop_index`; const lastIndexLabel = `@last_index`; const iteratorLabel = `@loop_next_iter`; const numberType = builtinTypes.get('number')!; const booleanType = builtinTypes.get('boolean')!; const anyType = builtinTypes.get('any')!; const indexExpr = new IdentifierExpression(loopIndexLabel); const iterExpr = new IdentifierExpression(iteratorLabel); let initializer: Statement; let cond: Expression; let incrementor: Expression; // TODO: use i32 for index if (isStaticExpr) { const loopIndex = new Variable(loopIndexLabel, numberType); const lastIndex = new Variable(lastIndexLabel, numberType); scope.addVariable(loopIndex); scope.addVariable(lastIndex); // const indexExpr = new IdentifierExpression(loopIndexLabel); indexExpr.setExprType(loopIndex.varType); const lastExpr = new IdentifierExpression(lastIndexLabel); lastExpr.setExprType(lastIndex.varType); const indexInitExpr = new BinaryExpression( ts.SyntaxKind.EqualsToken, indexExpr, new NumberLiteralExpression(0), ); indexInitExpr.setExprType(loopIndex.varType); const exprPropExpr = new IdentifierExpression('length'); exprPropExpr.setExprType(numberType); const propAccessExpr = new PropertyAccessExpression( expr, exprPropExpr, ); propAccessExpr.setExprType(numberType); const lastIndexInitExpr = new BinaryExpression( ts.SyntaxKind.EqualsToken, lastExpr, propAccessExpr, ); lastIndexInitExpr.setExprType(lastIndex.varType); scope.addStatement(new ExpressionStatement(lastIndexInitExpr)); initializer = new ExpressionStatement(indexInitExpr); cond = new BinaryExpression( ts.SyntaxKind.LessThanToken, indexExpr, lastExpr, ); cond.setExprType(booleanType); incrementor = new UnaryExpression( ts.SyntaxKind.PostfixUnaryExpression, ts.SyntaxKind.PlusPlusToken, indexExpr, ); incrementor.setExprType(numberType); } else { const loopIter = new Variable(iteratorLabel, anyType); scope.addVariable(loopIter); // const iterExpr = new IdentifierExpression(iteratorLabel); iterExpr.setExprType(loopIter.varType); // for dynamic array, should get its iterator firstly const tempIter = new Variable('@temp_iter', anyType); scope.addVariable(tempIter); const tempIterExpr = new IdentifierExpression('@temp_iter'); tempIterExpr.setExprType(tempIter.varType); // tempIter = expr.value(); const valueExpr = new IdentifierExpression('values'); valueExpr.setExprType(anyType); const valueAccessExpr = new PropertyAccessExpression( expr, valueExpr, ); valueAccessExpr.setExprType(anyType); const valueCallExpr = new CallExpression(valueAccessExpr); valueCallExpr.setExprType(anyType); const lengthPropExpr = new IdentifierExpression('length'); lengthPropExpr.setExprType(anyType); const lengthAccessExpr = new PropertyAccessExpression( expr, lengthPropExpr, ); lengthAccessExpr.setExprType(anyType); const isArray = new BinaryExpression( ts.SyntaxKind.ExclamationEqualsEqualsToken, lengthAccessExpr, new UndefinedKeywordExpression(), ); isArray.rightOperand.setExprType(anyType); isArray.setExprType(booleanType); // if isArray is true, assign expr.values() to tempIter const assignForArrayExpr = new BinaryExpression( ts.SyntaxKind.EqualsToken, tempIterExpr, valueCallExpr, ); assignForArrayExpr.setExprType(anyType); // if isArray is false, assign expr to tempIter const assignForNonArrayExpr = new BinaryExpression( ts.SyntaxKind.EqualsToken, tempIterExpr, expr, ); assignForNonArrayExpr.setExprType(anyType); const ifStmt = new IfStatement( isArray, new ExpressionStatement(assignForArrayExpr), new ExpressionStatement(assignForNonArrayExpr), ); scope.addStatement(ifStmt); const nextExpr = new IdentifierExpression('next'); nextExpr.setExprType(anyType); const iterNextExpr = new PropertyAccessExpression( tempIterExpr, nextExpr, ); iterNextExpr.setExprType(anyType); const callIterNextExpr = new CallExpression(iterNextExpr); callIterNextExpr.setExprType(anyType); const doneExpr = new IdentifierExpression('done'); doneExpr.setExprType(anyType); const iterDoneExpr = new PropertyAccessExpression( iterExpr, doneExpr, ); const initExpr = new BinaryExpression( ts.SyntaxKind.EqualsToken, iterExpr, callIterNextExpr, ); initExpr.setExprType(anyType); initializer = new ExpressionStatement(initExpr); cond = new UnaryExpression( ts.SyntaxKind.PrefixUnaryExpression, ts.SyntaxKind.ExclamationToken, iterDoneExpr, ); cond.setExprType(booleanType); incrementor = initExpr; } let statement = this.visitNode(forOfStmtNode.statement)!; if (!(statement instanceof BlockStatement)) { const blockScope = new Scope(scope); blockScope.statements.push(statement); statement = new BlockStatement(); statement.setScope(blockScope); } const scopeStmts = statement.getScope()!.statements; if (isStaticExpr) { const elemAccessExpr = new ElementAccessExpression(expr, indexExpr); elemAccessExpr.setExprType(elementExpr.exprType); const elemAssignmentExpr = new BinaryExpression( ts.SyntaxKind.EqualsToken, elementExpr, elemAccessExpr, ); elemAssignmentExpr.setExprType(elementExpr.exprType); scopeStmts.unshift(new ExpressionStatement(elemAssignmentExpr)); } else { const valueExpr = new IdentifierExpression('value'); valueExpr.setExprType(anyType); const valueAccessExpr = new PropertyAccessExpression( iterExpr, valueExpr, ); valueAccessExpr.setExprType(anyType); const valueAssignExpr = new BinaryExpression( ts.SyntaxKind.EqualsToken, elementExpr, valueAccessExpr, ); valueAssignExpr.setExprType(elementExpr.exprType); scopeStmts.unshift(new ExpressionStatement(valueAssignExpr)); } const forStatement = new ForStatement( loopLabel, blockLabel, this.continueFlagMap.has(continueLabel) ? continueLabel : null, cond, statement, initializer, incrementor, ); return forStatement; } private convertForInToForLoop( forInStmtNode: ts.ForInStatement, scope: Scope, loopLabel: string, blockLabel: string, continueLabel: string, ) { const propNameLabel = `@prop_name_arr`; const propNameArrExpr = new IdentifierExpression(propNameLabel); let elementExpr: IdentifierExpression; const forInInitializer = forInStmtNode.initializer; if (ts.isVariableDeclarationList(forInInitializer)) { elementExpr = this.parserCtx.expressionProcessor.visitNode( forInInitializer.declarations[0].name, ) as IdentifierExpression; } else { elementExpr = this.parserCtx.expressionProcessor.visitNode( forInInitializer, ) as IdentifierExpression; } const expr = this.parserCtx.expressionProcessor.visitNode( forInStmtNode.expression, ); const exprType = expr.exprType; /* the prop names array set `any` as its default type */ let propNamesArrType = builtinTypes.get('any')!; let getKeysExpr: Expression | undefined = undefined; if ( exprType.kind === TypeKind.CLASS || exprType.kind === TypeKind.INTERFACE ) { /* For class/interface, prop names array's type is Array(string) */ propNamesArrType = new TSArray(builtinTypes.get('string')!); if (exprType.kind === TypeKind.CLASS) { /* If expr has class type, its property name can be got in compile time */ getKeysExpr = new ArrayLiteralExpression( this.getClassIterPropNames(exprType as TSClass), ); } else if (exprType.kind === TypeKind.INTERFACE) { /* If expr has interface type, its property name should be got during runtime */ getKeysExpr = new EnumerateKeysExpression(expr); } } else if (exprType.kind === TypeKind.ANY) { propNamesArrType = builtinTypes.get('any')!; /* If expr has interface type, its property name should be got during runtime */ getKeysExpr = new EnumerateKeysExpression(expr); } if (!getKeysExpr) { throw new UnimplementError( `${exprType.kind} has not been supported in for in statement`, ); } /* insert temp array var to store property names */ const propNameArr = new Variable(propNameLabel, propNamesArrType); scope.addVariable(propNameArr); propNameArrExpr.setExprType(propNamesArrType); getKeysExpr.setExprType(propNamesArrType); const arrAssignExpr = new BinaryExpression( ts.SyntaxKind.EqualsToken, propNameArrExpr, getKeysExpr, ); arrAssignExpr.setExprType(propNamesArrType); scope.addStatement(new ExpressionStatement(arrAssignExpr)); const numberType = builtinTypes.get('number')!; const exprPropExpr = new IdentifierExpression('length'); exprPropExpr.setExprType(numberType); const propAccessExpr = new PropertyAccessExpression( propNameArrExpr, exprPropExpr, ); propAccessExpr.setExprType(numberType); const loopIndexLabel = `@loop_index`; const lastIndexLabel = `@last_index`; const loopIndex = new Variable(loopIndexLabel, numberType); const lastIndex = new Variable(lastIndexLabel, numberType); scope.addVariable(loopIndex); scope.addVariable(lastIndex); const indexExpr = new IdentifierExpression(loopIndexLabel); indexExpr.setExprType(loopIndex.varType); const lastExpr = new IdentifierExpression(lastIndexLabel); lastExpr.setExprType(lastIndex.varType); const indexInitExpr = new BinaryExpression( ts.SyntaxKind.EqualsToken, indexExpr, new NumberLiteralExpression(0), ); indexInitExpr.setExprType(loopIndex.varType); const lastIndexInitExpr = new BinaryExpression( ts.SyntaxKind.EqualsToken, lastExpr, propAccessExpr, ); lastIndexInitExpr.setExprType(lastIndex.varType); scope.addStatement(new ExpressionStatement(lastIndexInitExpr)); const initializer = new ExpressionStatement(indexInitExpr); const cond = new BinaryExpression( ts.SyntaxKind.LessThanToken, indexExpr, lastExpr, ); cond.setExprType(numberType); const incrementor = new UnaryExpression( ts.SyntaxKind.PostfixUnaryExpression, ts.SyntaxKind.PlusPlusToken, indexExpr, ); incrementor.setExprType(numberType); let statement = this.visitNode(forInStmtNode.statement)!; if (!(statement instanceof BlockStatement)) { const blockScope = new Scope(scope); blockScope.statements.push(statement); statement = new BlockStatement(); statement.setScope(blockScope); } const scopeStmts = statement.getScope()!.statements; const elemAccessExpr = new ElementAccessExpression( propNameArrExpr, indexExpr, ); elemAccessExpr.setExprType(elementExpr.exprType); const elemAssignmentExpr = new BinaryExpression( ts.SyntaxKind.EqualsToken, elementExpr, elemAccessExpr, ); elemAssignmentExpr.setExprType(elementExpr.exprType); scopeStmts.unshift(new ExpressionStatement(elemAssignmentExpr)); return new ForStatement( loopLabel, blockLabel, this.continueFlagMap.has(continueLabel) ? continueLabel : null, cond, statement, initializer, incrementor, ); } /** it uses for for in loop, to stores property names in compile time */ private getClassIterPropNames(classType: TSClass): Expression[] { const memberFields = classType.fields; return memberFields.map((field) => { const strLiteralExpr = new StringLiteralExpression(field.name); strLiteralExpr.setExprType(builtinTypes.get('string')!); return strLiteralExpr; }); } private getLoopContinueLabel(loopLabel: string): string { return loopLabel + '_continue'; } } export class StatementSpecializationProcessor { currentScope: Scope | null = null; constructor(private parserCtx: ParserContext) {} visit() { for (const g of this.parserCtx.globalScopes) { this.currentScope = g; this.visitScope(g); } } visitScope(scope: Scope) { switch (scope.kind) { case ScopeKind.FunctionScope: this.currentScope = scope as FunctionScope; if (scope.genericOwner) { const originalFuncType = ( scope.genericOwner as FunctionScope ).funcType; const typeParameters = originalFuncType.isMethod ? originalFuncType.belongedClass!.typeArguments ? originalFuncType.belongedClass!.typeArguments : originalFuncType.typeArguments! : originalFuncType.typeArguments!; const specializedFuncType = (scope as FunctionScope) .funcType; const typeArguments = specializedFuncType.isMethod ? specializedFuncType.belongedClass! .specializedArguments ? specializedFuncType.belongedClass! .specializedArguments : specializedFuncType.specializedArguments! : specializedFuncType.specializedArguments!; const genericFunctionScope = scope.genericOwner as FunctionScope; //prcocess parameters and variables genericFunctionScope.paramArray.forEach((v) => { let varType = v.varType; let initExpression = v.initExpression; if (typeArguments) { varType = processGenericType( v.varType, typeArguments, typeParameters, this.parserCtx, ); initExpression = initExpression ? this.parserCtx.expressionProcessor.specializeExpression( initExpression, typeArguments, typeParameters, scope, ) : initExpression; } const new_parameter = new Parameter( v.varName, varType, v.varModifiers, v.varIndex, v.isOptional, v.destructuring, initExpression, v.isLocalVar(), ); if (v.varIsClosure) new_parameter.setVarIsClosure(); (scope as FunctionScope).addParameter(new_parameter); }); genericFunctionScope.varArray.forEach((v, index) => { if (v.varName == '@context') { const contextVar = new Variable( '@context', v.varType, v.varModifiers, v.varIndex, v.isLocalVar(), v.initExpression, ); contextVar.scope = scope; (scope as FunctionScope).contextVariable = contextVar; scope.addVariable(contextVar); } else if (v.varName == 'this') { const thisVar = new Variable( 'this', processGenericType( v.varType, typeArguments, typeParameters, this.parserCtx, ), v.varModifiers, v.varIndex, v.isLocalVar(), v.initExpression, ); thisVar.setVarIsClosure(); thisVar.scope = scope; scope.addVariable(thisVar); } }); scope.genericOwner.statements.forEach((s) => { const stmt = this.processStatement( s, typeArguments, typeParameters, this.currentScope!, ); scope.addStatement(stmt); }); } break; default: this.foreachScopeChildren(scope); break; } } foreachScopeChildren(scope: Scope) { for (const c of scope.children) { this.currentScope = c; this.visitScope(c); } } processStatement( s: Statement, typeArguments: Type[], typeParameters: TSTypeParameter[], currentScope: Scope, ): Statement { const stmt = s.clone(); switch (stmt.statementKind) { case ts.SyntaxKind.VariableStatement: { const variableStatement = stmt as VariableStatement; const newVariableStatement = new VariableStatement(); variableStatement.varArray.forEach((v) => { const initExpression = v.initExpression; if (!initExpression) { newVariableStatement.addVariable(v); } else { const newInitExpression = this.parserCtx.expressionProcessor.specializeExpression( initExpression, typeArguments, typeParameters, currentScope, ); const newVar = new Variable( v.varName, newInitExpression.exprType, v.varModifiers, v.varIndex, v.isLocalVar(), newInitExpression, ); if (v.varIsClosure) newVar.setVarIsClosure(); newVariableStatement.addVariable(newVar); currentScope.addVariable(newVar); newVar.scope = currentScope; } }); return newVariableStatement; } case ts.SyntaxKind.IfStatement: { const ifStatement = stmt as IfStatement; const newIfCondition = this.parserCtx.expressionProcessor.specializeExpression( ifStatement.ifCondition, typeArguments, typeParameters, this.currentScope! as FunctionScope, ); const newIfTrue = this.processStatement( ifStatement.ifIfTrue, typeArguments, typeParameters, currentScope, ); let newIfFalse: Statement | null = null; if (ifStatement.ifIfFalse) { newIfFalse = this.processStatement( ifStatement.ifIfFalse, typeArguments, typeParameters, currentScope, ); } const newIfStatement = new IfStatement( newIfCondition, newIfTrue, newIfFalse, ); return newIfStatement; } case ts.SyntaxKind.Block: { const blockStatement = stmt as BlockStatement; const newBlockStatement = new BlockStatement(); if (blockStatement.getScope() !== null) { const genericBlockScope = blockStatement.getScope() as BlockScope; const newBlockScope = new BlockScope( currentScope, genericBlockScope.getName(), this.currentScope as FunctionScope, ); genericBlockScope.specialize(newBlockScope); // initialize the properties of BlockScope; newBlockScope.setGenericOwner(genericBlockScope); genericBlockScope.addSpecializedScope( genericBlockScope.getName(), newBlockScope, ); if (genericBlockScope.mangledName !== '') { newBlockScope.mangledName = currentScope.mangledName !== '' ? currentScope.mangledName + '|' + newBlockScope.getName() : newBlockScope.getName(); } //process variable '@context' genericBlockScope.varArray.forEach((v) => { if (v.varName == '@context') { const contextVar = new Variable( '@context', v.varType, v.varModifiers, v.varIndex, v.isLocalVar(), v.initExpression, ); contextVar.scope = newBlockScope; newBlockScope.addVariable(contextVar); } }); // processing statement genericBlockScope.statements.forEach((s) => { const newStmt = this.processStatement( s, typeArguments, typeParameters, newBlockScope, ); newBlockScope.addStatement(newStmt); }); newBlockStatement.setScope(newBlockScope); } return newBlockStatement; } case ts.SyntaxKind.ReturnStatement: { const returnStatement = stmt as ReturnStatement; if (!returnStatement.returnExpression) return returnStatement; const returnExpression = this.parserCtx.expressionProcessor.specializeExpression( returnStatement.returnExpression, typeArguments, typeParameters, this.currentScope! as FunctionScope, ); const newReturnStatement = new ReturnStatement( returnExpression, ); return newReturnStatement; } case ts.SyntaxKind.WhileStatement: { const baseLoopStatement = stmt as BaseLoopStatement; const newLoopLabel = baseLoopStatement.loopLabel; const newBlockLabel = baseLoopStatement.loopBlockLabel; const newContinueLable = baseLoopStatement.loopContinueLable; const newLoopCondtion = this.parserCtx.expressionProcessor.specializeExpression( baseLoopStatement.loopCondtion, typeArguments, typeParameters, currentScope, ); const newLoopBody = this.processStatement( baseLoopStatement.loopBody, typeArguments, typeParameters, currentScope, ); const newBaseLoopStatement = new BaseLoopStatement( baseLoopStatement.statementKind, newLoopLabel, newBlockLabel, newContinueLable, newLoopCondtion, newLoopBody, ); return newBaseLoopStatement; } case ts.SyntaxKind.ExpressionStatement: { const expressionStatement = stmt as ExpressionStatement; const expression = this.parserCtx.expressionProcessor.specializeExpression( expressionStatement.expression, typeArguments, typeParameters, currentScope, ); const newExpressionStatement = new ExpressionStatement( expression, ); return newExpressionStatement; } case ts.SyntaxKind.SwitchStatement: { const switchStatement = stmt as SwitchStatement; const newSwitchCondition = this.parserCtx.expressionProcessor.specializeExpression( switchStatement.switchCondition, typeArguments, typeParameters, currentScope, ); const newSwitchCaseBlock = this.processStatement( switchStatement.switchCaseBlock, typeArguments, typeParameters, currentScope, ); const newSwitchStatement = new SwitchStatement( newSwitchCondition, newSwitchCaseBlock, ); return newSwitchStatement; } case ts.SyntaxKind.CaseBlock: { const caseBlock = stmt as CaseBlock; const newSwitchLabel = caseBlock.switchLabel; const newBreakLabel = caseBlock.breakLabel; const stmtArray: Statement[] = []; caseBlock.caseCauses.forEach((s) => { const newStmt = this.processStatement( s, typeArguments, typeParameters, currentScope, ); stmtArray.push(newStmt); }); const newCaseBlock = new CaseBlock( newSwitchLabel, newBreakLabel, stmtArray, ); return newCaseBlock; } case ts.SyntaxKind.CaseClause: { const caseClause = stmt as CaseClause; const newCaseExpr = this.parserCtx.expressionProcessor.specializeExpression( caseClause.caseExpr, typeArguments, typeParameters, currentScope, ); const stmtArray: Statement[] = []; caseClause.caseStatements.forEach((s) => { const newStmt = this.processStatement( s, typeArguments, typeParameters, currentScope, ); stmtArray.push(newStmt); }); const newCaseClause = new CaseClause(newCaseExpr, stmtArray); return newCaseClause; } case ts.SyntaxKind.DefaultClause: { const defaultClause = stmt as DefaultClause; const stmtArray: Statement[] = []; defaultClause.caseStatements.forEach((s) => { const newStmt = this.processStatement( s, typeArguments, typeParameters, currentScope, ); stmtArray.push(newStmt); }); const newDefaultClause = new DefaultClause(stmtArray); return newDefaultClause; } case ts.SyntaxKind.ThrowStatement: { const throwStatement = stmt as ThrowStatement; const newExpr = this.parserCtx.expressionProcessor.specializeExpression( throwStatement.expr, typeArguments, typeParameters, currentScope, ); const newThrowStatement = new ThrowStatement(newExpr); return newThrowStatement; } case ts.SyntaxKind.CatchClause: { const catchClauseStatement = stmt as CatchClauseStatement; const newCatchBlockStmt = this.processStatement( catchClauseStatement.catchBlockStmt, typeArguments, typeParameters, currentScope, ) as BlockStatement; const newCatchClauseStatement = new CatchClauseStatement( newCatchBlockStmt, ); return newCatchClauseStatement; } case ts.SyntaxKind.TryStatement: { const tryStatement = stmt as TryStatement; const newLable = tryStatement.label; const newTryBlockStmt = this.processStatement( tryStatement.tryBlockStmt, typeArguments, typeParameters, currentScope, ) as BlockStatement; const newTryStatement = new TryStatement( newLable, newTryBlockStmt, ); return newTryStatement; } default: return stmt; } } } ================================================ FILE: src/type.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import ts from 'typescript'; import { ParserContext } from './frontend.js'; import { BlockScope, ClassScope, ClosureEnvironment, FunctionScope, GlobalScope, NamespaceScope, Scope, ScopeKind, } from './scope.js'; import { Parameter, Variable } from './variable.js'; import { Expression } from './expression.js'; import { Logger } from './log.js'; import { DefaultTypeId, MutabilityKind, NullabilityKind, PackedTypeKind, processGenericType, isTypeGeneric, createScopeBySpecializedType, isWASMArrayComment, isWASMStructComment, parseCommentBasedTypeAliasNode, } from './utils.js'; import { CommentError, TypeError } from './error.js'; import { BuiltinNames } from '../lib/builtin/builtin_name.js'; export const enum TypeKind { VOID = 'void', BOOLEAN = 'boolean', NUMBER = 'number', ANY = 'any', UNDEFINED = 'undefined', STRING = 'string', ARRAY = 'array', FUNCTION = 'function', CLASS = 'class', ENUM = 'enum', NULL = 'null', INTERFACE = 'interface', UNION = 'unoin', TUPLE = 'tuple', WASM_I32 = 'i32', WASM_I64 = 'i64', WASM_F32 = 'f32', WASM_F64 = 'f64', WASM_ANYREF = 'anyref', WASM_ARRAY = 'wasm_array', WASM_STRUCT = 'wasm_struct', GENERIC = 'generic', UNKNOWN = 'unknown', CONTEXT = 'context', TYPE_PARAMETER = 'type_parameter', } export class Type { protected typeKind = TypeKind.UNKNOWN; isPrimitive = false; isWasmType = false; get kind(): TypeKind { return this.typeKind; } toString(): string { return `TYPE(${this.typeKind})`; } get isDeclare(): boolean { return false; } } export class WasmType extends Type { name: string; constructor(private type: string) { super(); this.name = type; this.isWasmType = true; switch (type) { case 'i32': { this.typeKind = TypeKind.WASM_I32; break; } case 'i64': { this.typeKind = TypeKind.WASM_I64; break; } case 'f32': { this.typeKind = TypeKind.WASM_F32; break; } case 'f64': { this.typeKind = TypeKind.WASM_F64; break; } case 'anyref': { this.typeKind = TypeKind.WASM_ANYREF; break; } case TypeKind.WASM_ARRAY: { this.typeKind = TypeKind.WASM_ARRAY; break; } case TypeKind.WASM_STRUCT: { this.typeKind = TypeKind.WASM_STRUCT; break; } default: { this.typeKind = TypeKind.UNKNOWN; break; } } } public getName(): string { return this.name; } } export class WasmArrayType extends WasmType { arrayType: TSArray; packedTypeKind: PackedTypeKind = PackedTypeKind.Not_Packed; mutability: MutabilityKind = MutabilityKind.Mutable; nullability: NullabilityKind = NullabilityKind.Nullable; constructor( arrayType: TSArray, packedTypeKind?: PackedTypeKind, mutability?: MutabilityKind, nullability?: NullabilityKind, ) { super(TypeKind.WASM_ARRAY); this.arrayType = arrayType; if (packedTypeKind) { this.packedTypeKind = packedTypeKind; } if (mutability) { this.mutability = mutability; } if (nullability) { this.nullability = nullability; } } } export class WasmStructType extends WasmType { tupleType: TSTuple; packedTypeKinds: PackedTypeKind[]; mutabilitys: MutabilityKind[]; nullability: NullabilityKind = NullabilityKind.Nullable; baseType: WasmStructType | undefined = undefined; constructor( tupleType: TSTuple, packedTypeKinds?: PackedTypeKind[], mutabilitys?: MutabilityKind[], nullability?: NullabilityKind, baseType?: WasmStructType, ) { super(TypeKind.WASM_STRUCT); this.tupleType = tupleType; if (packedTypeKinds) { this.packedTypeKinds = packedTypeKinds; } else { this.packedTypeKinds = new Array( this.tupleType.elements.length, ); this.packedTypeKinds.fill(PackedTypeKind.Not_Packed); } if (mutabilitys) { this.mutabilitys = mutabilitys; } else { this.mutabilitys = new Array( this.tupleType.elements.length, ); this.mutabilitys.fill(MutabilityKind.Mutable); } if (nullability) { this.nullability = nullability; } this.baseType = baseType; } } export class GenericType extends Type { constructor() { super(); this.typeKind = TypeKind.GENERIC; } } export class Primitive extends Type { constructor(private type: string) { super(); this.isPrimitive = true; switch (type) { case 'number': { this.typeKind = TypeKind.NUMBER; break; } case 'string': { this.typeKind = TypeKind.STRING; break; } case 'boolean': { this.typeKind = TypeKind.BOOLEAN; break; } case 'any': { this.typeKind = TypeKind.ANY; break; } case 'undefined': { this.typeKind = TypeKind.UNDEFINED; break; } case 'void': { this.typeKind = TypeKind.VOID; break; } case 'null': { this.typeKind = TypeKind.NULL; break; } default: { this.typeKind = TypeKind.UNKNOWN; } } } toString(): string { return this.type; } } export class TSContext extends Type { constructor( public parentCtxType?: TSContext, public freeVarTypeList: Type[] = [], ) { super(); this.typeKind = TypeKind.CONTEXT; } toString(): string { const typeName = 'ContextType'; if (this.parentCtxType) { typeName.concat('_'); typeName.concat(this.parentCtxType.toString()); } for (const freeVarType of this.freeVarTypeList) { typeName.concat('_'); typeName.concat(freeVarType.toString()); } return typeName; } } export const builtinTypes = new Map([ ['number', new Primitive('number')], ['string', new Primitive('string')], ['boolean', new Primitive('boolean')], ['any', new Primitive('any')], ['undefined', new Primitive('undefined')], ['void', new Primitive('void')], ['null', new Primitive('null')], ['undefined', new Primitive('undefined')], ['generic', new GenericType()], ]); export const builtinWasmTypes = new Map([ ['i32', new WasmType('i32')], ['f32', new WasmType('f32')], ['i64', new WasmType('i64')], ['f64', new WasmType('f64')], ['anyref', new WasmType('anyref')], ]); // type for template export class TSTypeParameter extends Type { typeKind = TypeKind.TYPE_PARAMETER; private _name: string; // the name of TypeParameter private _wide: Type; // the wide type of this type parameter private _default?: Type; // the default type of this type parameter private _index: number; // the declaration index, important! constructor(name: string, wide: Type, index: number, def?: Type) { super(); this._name = name; this._wide = wide; this._index = index; this._default = def; } get name(): string { return this._name; } get wideType(): Type { return this._wide; } get index(): number { return this._index; } get defaultType(): Type | undefined { return this._default; } toString(): string { return `TypeParameter(${this._name} wide:${this._wide} default: ${this._default})`; } } export interface TsClassField { name: string; type: Type; modifier?: 'readonly'; visibility?: 'public' | 'protected' | 'private'; static?: 'static'; optional?: boolean; } export const enum FunctionKind { DEFAULT = 'default', CONSTRUCTOR = 'constructor', METHOD = 'method', GETTER = 'getter', SETTER = 'setter', STATIC = 'static', } export function getMethodPrefix(kind: FunctionKind): string { switch (kind) { case FunctionKind.CONSTRUCTOR: return 'constructor'; case FunctionKind.GETTER: return 'get_'; case FunctionKind.SETTER: return 'set_'; default: return ''; } } export interface TsClassFunc { name: string; type: TSFunction; optional?: boolean; } export interface ClassMethod { index: number; method: TsClassFunc | null; } export class TSTypeWithArguments extends Type { private _typeArguments?: TSTypeParameter[]; private _specializedArguments?: Type[]; private _genericOwner?: TSTypeWithArguments; private _belongedScope?: Scope; constructor() { super(); } get typeArguments(): TSTypeParameter[] | undefined { return this._typeArguments; } addTypeParameter(type: TSTypeParameter) { if (!this._typeArguments) this._typeArguments = []; if ( !this._typeArguments.find( (t) => t.name == type.name && t.wideType.kind == type.wideType.kind, ) ) this._typeArguments.push(type); // ignore the index } setTypeParameters(types: TSTypeParameter[] | undefined) { this._typeArguments = types; } getTypeParameter(name: string): TSTypeParameter | undefined { if (this._typeArguments) { const param = this._typeArguments.find((p) => p.name == name); if (param) return param; } return undefined; } get genericOwner(): TSTypeWithArguments | undefined { return this._genericOwner; } setGenericOwner(owner: TSTypeWithArguments | undefined) { this._genericOwner = owner; } get belongedScope(): Scope | undefined { return this._belongedScope; } setBelongedScope(scope: Scope | undefined) { this._belongedScope = scope; } get specializedArguments(): Type[] | undefined { return this._specializedArguments; } setSpecializedArguments(types: Type[] | undefined) { this._specializedArguments = types; } } const enum TraverseStatus { NOTVISITTED, VISITTED, PROCESSED, } export class TSClass extends TSTypeWithArguments { typeKind = TypeKind.CLASS; traverseStatus = TraverseStatus.NOTVISITTED; private _typeId = DefaultTypeId; private _name = ''; private _mangledName = ''; private _memberFields: Array = []; private _staticFields: Array = []; private _methods: Array = []; private _baseClass: TSClass | null = null; private _drivedClasses: TSClass[] | null = null; private implInfc: TSInterface | null = null; private _isLiteral = false; private _ctor: TSFunction | null = null; public hasDeclareCtor = true; private _isDeclare = false; private _numberIndexType?: Type; private _stringIndexType?: Type; public staticFieldsInitValueMap: Map = new Map(); /* override or own methods */ public overrideOrOwnMethods: Set = new Set(); constructor() { super(); this.typeKind = TypeKind.CLASS; } toString(): string { return `Class(${this._name}(${this._mangledName} ${ this._isLiteral ? 'Literal' : '' }))`; } get fields(): Array { return this._memberFields; } get staticFields(): TsClassField[] { return this._staticFields; } get memberFuncs(): Array { return this._methods; } set ctorType(ctor: TSFunction) { this._ctor = ctor; } get ctorType(): TSFunction { return this._ctor!; } get isDeclare(): boolean { return this._isDeclare; } set isDeclare(value: boolean) { this._isDeclare = value; } get numberIndexType(): Type | undefined { return this._numberIndexType; } setNumberIndexType(type: Type) { this._numberIndexType = type; } get stringIndexType(): Type | undefined { return this._stringIndexType; } setStringIndexType(type: Type) { this._stringIndexType = type; } setBase(base: TSClass): void { this._baseClass = base; } getBase(): TSClass | null { return this._baseClass; } setDrivedClass(drived: TSClass): void { if (!this._drivedClasses) this._drivedClasses = []; this._drivedClasses.push(drived); } getDrivedClasses(): TSClass[] | null { return this._drivedClasses; } setImplInfc(infc: TSInterface | null): void { this.implInfc = infc; } getImplInfc(): TSInterface | null { return this.implInfc; } addMemberField(memberField: TsClassField): void { this._memberFields.push(memberField); } getMemberField(name: string): TsClassField | null { return ( this._memberFields.find((f) => { return f.name === name; }) || null ); } getMemberFieldIndex(name: string): number { return this._memberFields.findIndex((f) => { return f.name === name; }); } addStaticMemberField(memberField: TsClassField): void { this._staticFields.push(memberField); } getStaticMemberField(name: string): TsClassField | null { return ( this._staticFields.find((f) => { return f.name === name; }) || null ); } getStaticFieldIndex(name: string): number { return this._staticFields.findIndex((f) => { return f.name === name; }); } addMethod(classMethod: TsClassFunc): void { classMethod.type.isMethod = true; // when sub class inherits the method of the base class, it should not modify the 'belongedClass'. if (!classMethod.type.belongedClass) classMethod.type.belongedClass = this; this._methods.push(classMethod); } getMethod( name: string, kind: FunctionKind = FunctionKind.METHOD, ): ClassMethod { const res = this.memberFuncs.findIndex((f) => { return name === f.name && kind === f.type.funcKind; }); if (res !== -1) { return { index: res, method: this.memberFuncs[res] }; } return { index: -1, method: null }; } updateMethod( name: string, kind: FunctionKind = FunctionKind.METHOD, funcType: TSFunction, ): boolean { const res = this.memberFuncs.findIndex((f) => { return name === f.name && kind === f.type.funcKind; }); if (res !== -1) { this.memberFuncs[res].type = funcType; return true; } return false; } setClassName(name: string) { this._name = name; } get className(): string { return this._name; } get mangledName(): string { return this._mangledName; } set mangledName(name: string) { this._mangledName = name; } set typeId(id: number) { this._typeId = id; } get typeId() { return this._typeId; } set isLiteral(b: boolean) { this._isLiteral = b; } get isLiteral(): boolean { return this._isLiteral; } } export class TSInterface extends TSClass { constructor() { super(); this.typeKind = TypeKind.INTERFACE; } } export class TSArray extends Type { constructor(private _elemType: Type) { super(); this.typeKind = TypeKind.ARRAY; } set elementType(type: Type) { this._elemType = type; } get elementType(): Type { return this._elemType; } toString(): string { return `Array<${this._elemType}>`; } } export class TSFunction extends TSTypeWithArguments { typeKind = TypeKind.FUNCTION; private _parameterTypes: Type[] = []; private _isOptionalParams: boolean[] = []; private _returnType: Type = new Primitive('void'); // iff last parameter is rest paremeter private _restParamIdex = -1; private _isMethod = false; private _isDeclare = false; private _isStatic = false; private _isBinaryenImpl = false; private _isExport = false; // iff the function is a member function of a class private _belongedClass?: TSClass = undefined; toString(): string { const s: string[] = []; this._parameterTypes.forEach((t) => s.push(t.toString())); return `Function(${s.join(',')})${this._returnType}`; } constructor(public funcKind: FunctionKind = FunctionKind.DEFAULT) { super(); this.typeKind = TypeKind.FUNCTION; } set returnType(type: Type) { this._returnType = type; } get returnType(): Type { return this._returnType; } addParamType(paramType: Type) { this._parameterTypes.push(paramType); } setParamTypes(paramTypes: Type[]) { this._parameterTypes = paramTypes; } getParamTypes(): Type[] { return this._parameterTypes; } addIsOptionalParam(isOptional: boolean) { this._isOptionalParams.push(isOptional); } get isOptionalParams() { return this._isOptionalParams; } set restParamIdx(idx: number) { this._restParamIdex = idx; } get restParamIdx() { return this._restParamIdex; } hasRest() { return this._restParamIdex >= 0; } get isMethod() { return this._isMethod; } set isMethod(value: boolean) { this._isMethod = value; } get isDeclare(): boolean { return this._isDeclare; } set isDeclare(value: boolean) { this._isDeclare = value; } get isBinaryenImpl() { return this._isBinaryenImpl; } set isBinaryenImpl(value: boolean) { this._isBinaryenImpl = value; } get isStatic() { return this._isStatic; } set isStatic(value: boolean) { this._isStatic = value; } get isExport() { return this._isExport; } set isExport(value: boolean) { this._isExport = value; } set belongedClass(clazz: TSClass | undefined) { this._belongedClass = clazz; } get belongedClass() { return this._belongedClass; } // shadow copy, content of parameterTypes and returnType is not copied public clone(): TSFunction { const func = new TSFunction(this.funcKind); func.typeKind = this.typeKind; func._isBinaryenImpl = this._isBinaryenImpl; func._isDeclare = this._isDeclare; func._isExport = this._isExport; func._isMethod = this._isMethod; func._isOptionalParams = this._isOptionalParams; func._isStatic = this._isStatic; func._parameterTypes = this._parameterTypes; func._restParamIdex = this._restParamIdex; func._returnType = this._returnType; func.setTypeParameters(this.typeArguments); func.setBelongedScope(this.belongedScope); func.belongedClass = this.belongedClass; return func; } } export class TSUnion extends Type { typeKind = TypeKind.UNION; _types: Type[] = []; constructor() { super(); } get types(): Array { return this._types; } addType(type: Type) { this._types.push(type); } toString(): string { const s: string[] = []; this._types.forEach((t) => s.push(t.toString())); return `Union(${s.join(' | ')})`; } } const MixEnumMemberType: Type = (function () { const union = new TSUnion(); union.addType(builtinTypes.get('number')!); union.addType(builtinTypes.get('string')!); return union; })(); export class TSEnum extends Type { typeKind = TypeKind.ENUM; private _name: string; private _memberType: Type = builtinTypes.get('undefined')!; private _members: Map = new Map(); constructor(name: string) { super(); this._name = name; } get name(): string { return this._name; } get memberType(): Type { return this._memberType; } addMember(name: string, value: number | string) { if (this._members.has(name)) { throw new TypeError(`EnumMember exist: ${name}`); } this._members.set(name, value); if (this._memberType.kind == TypeKind.UNDEFINED) { if (typeof value == 'string') { this._memberType = builtinTypes.get('string')!; } else { this._memberType = builtinTypes.get('number')!; } } else if ( (this._memberType.kind == TypeKind.STRING && typeof value != 'string') || (this._memberType.kind == TypeKind.NUMBER && typeof value != 'number') ) { this._memberType = MixEnumMemberType; } } getMember(name: string): number | string | undefined { return this._members.get(name); } get members(): Map { return this._members; } toString(): string { let i = 0; let s = ''; this._members.forEach((v, k) => { if (i < 4) { s += k + ','; i++; } else if (i == 4) { s = s + '...'; } }); return `Enum(${s})`; } } export class TSTuple extends Type { typeKind = TypeKind.TUPLE; private _elements: Type[] = []; constructor() { super(); } get elements(): Array { return this._elements; } addType(type: Type) { this._elements.push(type); } toString(): string { const s: string[] = []; this._elements.forEach((t) => s.push(t.toString())); return `Tuple[${s.join(' , ')}]`; } } interface TsCustomType { tsType: ts.Type; customName: string; } export class TypeResolver { typechecker: ts.TypeChecker | undefined = undefined; globalScopes: Array; currentScope: Scope | null = null; nodeScopeMap: Map; tsTypeMap = new Map(); private symbolTypeMap = new Map(); nodeTypeCache = new Map(); static specializedTypeCache = new Map[]>(); static typeParameterIndex = 0; private loopEntry: TSClass | null = null; private typeRefsMap = new Map>(); /** when parsing class type, its base type must be already parsed */ private parsedClassTypes = new Set(); typeParameterStack: TSTypeParameter[] = []; constructor(private parserCtx: ParserContext) { this.nodeScopeMap = this.parserCtx.nodeScopeMap; this.globalScopes = this.parserCtx.globalScopes; } visitSymbolNode(fileList: ts.SourceFile[]) { this.typechecker = this.parserCtx.typeChecker; for (const file of fileList) { ts.forEachChild(file, (node) => { this.visitObjectSymbolNode(node); }); } } visit() { /** create Type and add it to scope */ this.nodeScopeMap.forEach((scope, node) => { ts.forEachChild(node, this.visitNode.bind(this)); }); /** parse reference relationship */ this.symbolTypeMap.forEach((type, symbolNode) => { if (type instanceof TSClass) { this.parseTypeRefRelationship(type as TSClass); } }); /** allocate type id for Class/Interface */ this.symbolTypeMap.forEach((type, symbolNode) => { if (type instanceof TSClass) { this.typeIdAllocate(type as TSClass); } }); } /** parse types with symbol value for TSClass */ private visitObjectSymbolNode(node: ts.Node) { let type: Type | undefined = undefined; let symbolNode = node; if (ts.isClassDeclaration(node)) { type = new TSClass(); } else if (ts.isInterfaceDeclaration(node)) { type = new TSInterface(); } else if (ts.isObjectLiteralExpression(node)) { type = new TSClass(); } else if ( ts.isVariableDeclaration(node) || ts.isParameter(node) || ts.isTypeLiteralNode(node) ) { const tsType = this.typechecker!.getTypeAtLocation(node); if (this.isObjectLiteral(tsType) || this.isObjectType(tsType)) { const symbol = tsType.symbol; if (symbol && symbol.declarations) { symbolNode = symbol.declarations[0]; type = new TSClass(); } } } if (type && !this.symbolTypeMap.has(symbolNode)) { this.symbolTypeMap.set(symbolNode, type); } ts.forEachChild(node, this.visitObjectSymbolNode.bind(this)); } private visitNode(node: ts.Node) { this.currentScope = this.parserCtx.getScopeByNode(node)!; switch (node.kind) { case ts.SyntaxKind.VariableDeclaration: case ts.SyntaxKind.Parameter: case ts.SyntaxKind.TypeLiteral: { const tsType = this.typechecker!.getTypeAtLocation(node); let type: Type; let symbolNode = node; // parse object types circular reference if ( (this.isObjectLiteral(tsType) || this.isObjectType(tsType)) && tsType.symbol && tsType.symbol.declarations ) { symbolNode = tsType.symbol.declarations[0]; type = this.symbolTypeMap.get(symbolNode)!; this.parseObjectType(symbolNode, type as TSClass); if ( tsType.aliasTypeArguments && this.noTypeParmeters(tsType.aliasTypeArguments) ) { const aliasTypes = tsType.aliasTypeArguments.map( (t) => { return this.tsTypeToType(t); }, ); const specificType = processGenericType( type, aliasTypes, (type as TSClass).typeArguments!, this.parserCtx, ) as TSClass; specificType.isLiteral = true; // TODO: in this case, specificType can't be recursive this.typeIdAllocate(specificType); this.symbolTypeMap.set(node, specificType); this.parsedClassTypes.add(specificType); this.addTypeToScopeTypeMap(specificType, node); } } else { type = this.generateNodeType(symbolNode); // if type is generic, may need to deal with the generic chain if (isTypeGeneric(type)) { if (type instanceof TSTypeWithArguments) { let typeArguments: Type[] = []; const typeParameters = type.typeArguments; // e.g. // type ItemGenerator = (item: T, index: U) => void // function test_func(func: ItemGenerator, a: T, b: U) {...} if (tsType.aliasTypeArguments) { typeArguments = tsType.aliasTypeArguments!.map( (t) => { return this.tsTypeToType(t); }, ); } else if (this.isTypeReference(tsType)) { // e.g. // class Foo {...}; // function bar(a: Foo, data: Y[]) {...} typeArguments = this.typechecker!.getTypeArguments( tsType as ts.TypeReference, ).map((t) => { return this.tsTypeToType(t); }); } if (typeParameters) type = processGenericType( type, typeArguments, typeParameters, this.parserCtx, ); } } } const customTypeName = this.getTsTypeRawName(node); this.addTypeToTSTypeMap( tsType, customTypeName ? customTypeName : '', type, ); this.addTypeToScopeTypeMap(type, symbolNode); break; } case ts.SyntaxKind.ObjectLiteralExpression: { const type = this.symbolTypeMap.get(node)!; this.parseObjectType(node, type as TSClass); this.addTypeToScopeTypeMap(type, node); break; } case ts.SyntaxKind.ClassDeclaration: { const classType = this.symbolTypeMap.get(node)!; this.parseClassType( node as ts.ClassDeclaration, classType as TSClass, ); this.addTypeToScopeTypeMap(classType, node); break; } case ts.SyntaxKind.InterfaceDeclaration: { const infcType = this.symbolTypeMap.get(node)!; const type = this.parseInfcType( node as ts.InterfaceDeclaration, infcType as TSInterface, ); this.addTypeToScopeTypeMap(type, node); this.symbolTypeMap.set(node, type); return; } case ts.SyntaxKind.UnionType: { const unionTypeNode = node as ts.UnionTypeNode; const tsType = this.typechecker!.getTypeFromTypeNode(unionTypeNode); const type = this.parseUnionType( tsType as ts.UnionType, unionTypeNode, ); this.addTypeToScopeTypeMap(type, node); break; } case ts.SyntaxKind.EnumDeclaration: { const type = this.parseEnumType(node as ts.EnumDeclaration); this.addTypeToScopeTypeMap(type, node); break; } case ts.SyntaxKind.FunctionDeclaration: case ts.SyntaxKind.FunctionExpression: case ts.SyntaxKind.ArrowFunction: { const type = this.generateNodeType(node); this.addTypeToScopeTypeMap(type, node); break; } case ts.SyntaxKind.TypeAliasDeclaration: { const typeAliasNode = node; const typeName = typeAliasNode.name.getText(); let type: Type; const tsType = this.typechecker!.getTypeAtLocation(typeAliasNode); /* 1. check if typeName is in builtinWasmType */ if (builtinWasmTypes.has(typeName)) { type = builtinWasmTypes.get(typeName)!; } else { const parseRes = parseCommentBasedTypeAliasNode(typeAliasNode); type = this.generateNodeType(typeAliasNode); /* 2. check if type is WASMArray/WASMStruct type */ if ( parseRes && (isWASMArrayComment(parseRes) || isWASMStructComment(parseRes)) ) { if (isWASMArrayComment(parseRes)) { if (!(type instanceof TSArray)) { throw new CommentError( `${type.toString()} is not array type`, ); } type = new WasmArrayType( type, parseRes.packedType, parseRes.mutability, parseRes.nullability, ); } else { if (!(type instanceof TSTuple)) { throw new CommentError( `${type.toString()} is not tuple type`, ); } let findRes: Type | undefined = undefined; if (parseRes.baseTypeName) { findRes = this.currentScope!.findType( parseRes.baseTypeName, ); } const baseType: WasmStructType | undefined = findRes ? (findRes as WasmStructType) : undefined; type = new WasmStructType( type, parseRes.packedTypes, parseRes.mutabilitys, parseRes.nullability, baseType, ); } } else { /* 3. check if type is TSTypeWithArguments */ if ( tsType.aliasTypeArguments && type instanceof TSTypeWithArguments ) { const typeArgs = tsType.aliasTypeArguments.map( (t) => { return this.tsTypeToType( t, ) as TSTypeParameter; }, ); type.setTypeParameters(typeArgs); } } } this.addTypeToTSTypeMap(tsType, typeName, type); this.currentScope!.addType(typeName, type); break; } case ts.SyntaxKind.AsExpression: { const asExprNode = node; const typeNode = asExprNode.type; const type = this.generateNodeType(typeNode); this.addTypeToScopeTypeMap(type, node); break; } default: break; } ts.forEachChild(node, this.visitNode.bind(this)); } private getTypeFromTSTypeMap(tsType: ts.Type, customName: string) { for (const tsCustomType of this.tsTypeMap.keys()) { if ( tsCustomType.tsType === tsType && tsCustomType.customName === customName ) { return this.tsTypeMap.get(tsCustomType); } } return undefined; } private addTypeToTSTypeMap( tsType: ts.Type, customName: string, type: Type, ) { for (const tsCustomType of this.tsTypeMap.keys()) { if ( tsCustomType.tsType === tsType && tsCustomType.customName === customName ) { this.tsTypeMap.set(tsCustomType, type); return; } } const typeObj: TsCustomType = { tsType: tsType, customName: customName, }; this.tsTypeMap.set(typeObj, type); return; } private addTypeToScopeTypeMap(type: Type, node: ts.Node) { const tsTypeString = this.getTsTypeName(node); if ( this.currentScope!.kind === ScopeKind.FunctionScope && type.kind === TypeKind.FUNCTION && ts.isFunctionLike(node) ) { (this.currentScope!).setFuncType(type as TSFunction); (type as TSFunction).setBelongedScope(this.currentScope!); } if (ts.isClassDeclaration(node)) { this.currentScope!.parent!.addType(tsTypeString, type); if (this.currentScope! instanceof ClassScope) { this.currentScope!.setClassType(type as TSClass); (type as TSClass).setBelongedScope(this.currentScope!); } } else if (ts.isObjectLiteralExpression(node)) { if (this.currentScope!.parent) { this.currentScope!.parent!.addType(tsTypeString, type); } if (this.currentScope! instanceof ClassScope) { this.currentScope!.setClassType(type as TSClass); } } else { this.currentScope!.addType(tsTypeString, type); } } getTsTypeRawName(node: ts.Node, isReturnType = false): string | undefined { if (ts.isTypeNode(node)) { return node.getText(); } else if (ts.isTypeReferenceNode(node)) { /* If one node is FunctionLike, then its type will be its return type */ if (!ts.isFunctionLike(node.parent) || isReturnType) { const typeRawName = node.getText(); return typeRawName; } } else if ((node as any).type) { return this.getTsTypeRawName((node as any).type, isReturnType); } return undefined; } getTsTypeName(node: ts.Node) { let tsTypeString: string | undefined = undefined; if (ts.isFunctionLike(node)) { /* if node is function like, then it must stored in nodeTypeCache */ tsTypeString = this.nodeTypeCache.get(node)!.toString(); } else { tsTypeString = this.getTsTypeRawName(node); } if (!tsTypeString) { tsTypeString = this.typechecker!.typeToString( this.typechecker!.getTypeAtLocation(node), ); } return tsTypeString!; } getTypeByInitializer(node: ts.Node): Type | undefined { if (ts.isPropertyAssignment(node)) { const initTypeName = this.getTsTypeRawName(node.initializer); const initTsType = this.typechecker!.getTypeAtLocation( node.initializer, ); return this.getTypeFromTSTypeMap( initTsType, initTypeName ? initTypeName : '', ); } return undefined; } generateNodeType(node: ts.Node): Type { if (!this.typechecker) { this.typechecker = this.parserCtx.typeChecker; } /* 1. get type from cached type */ const cached_type = this.nodeTypeCache.get(node); if (cached_type) { return cached_type; } /* 2. For wasmType, some node type should be equal with its initializer type */ const initializerType = this.getTypeByInitializer(node); if (initializerType) { return initializerType; } /* 3. special node kind should be parsed as signature */ if (ts.isConstructSignatureDeclaration(node)) { return this.parseSignature( this.typechecker!.getSignatureFromDeclaration( node as ts.ConstructSignatureDeclaration, )!, ); } if (ts.isCallSignatureDeclaration(node)) { return this.parseSignature( this.typechecker!.getSignatureFromDeclaration( node as ts.CallSignatureDeclaration, )!, ); } if (ts.isSetAccessorDeclaration(node)) { return this.parseSignature( this.typechecker!.getSignatureFromDeclaration( node as ts.SetAccessorDeclaration, )!, ); } if (ts.isGetAccessorDeclaration(node)) { return this.parseSignature( this.typechecker!.getSignatureFromDeclaration( node as ts.GetAccessorDeclaration, )!, ); } /* 4. handle this type */ let tsType = this.typechecker!.getTypeAtLocation(node); if ('isThisType' in tsType && (tsType as any).isThisType) { /* For "this" keyword, tsc will inference the actual type */ tsType = this.typechecker!.getDeclaredTypeOfSymbol(tsType.symbol); } /* 5. customed type should find from tsTypeMap firstly */ const tsTypeRawName = this.getTsTypeRawName(node); const parsedType = this.getTypeFromTSTypeMap( tsType, tsTypeRawName ? tsTypeRawName : '', ); if (parsedType) { return parsedType; } /* 6. parse tsType to type */ let type = this.tsTypeToType(tsType, node); /* 7. set right value's type based on left value's type */ if (type instanceof TSArray || type instanceof TSTuple) { /** Example: a: string[] = new Array() * the type of new Array() should be string[], instead of any[] */ /** Example: const a: [i32, string] = [1, 'hi']; * the type of [1, 'hi'] should be [i32, string], instead of [number, string] */ const parentNode = node.parent; if ( ts.isVariableDeclaration(parentNode) || ts.isBinaryExpression(parentNode) || ts.isPropertyDeclaration(parentNode) ) { type = this.generateNodeType(parentNode); } if ( ts.isNewExpression(parentNode) || ts.isArrayLiteralExpression(parentNode) ) { const parentType = this.generateNodeType(parentNode); if (type instanceof TSArray) { if (parentType.kind == TypeKind.ANY) { type = parentType; } else { type = (parentType).elementType; } } else { if (ts.isArrayLiteralExpression(parentNode)) { if (parentType instanceof TSTuple) { const parentFieldLength = parentType.elements.length; for (let i = 0; i < parentFieldLength; i++) { if (parentNode.elements[i] === node) { type = (parentType).elements[i]; break; } } } else { type = (parentType).elementType; } } } } } this.nodeTypeCache.set(node, type); return type; } public tsTypeToType( tsType: ts.Type, tsNode?: ts.Node, isReturnType = false, ): Type { let res: Type | undefined; const typeNode: ts.TypeNode | undefined = tsNode ? ts.isTypeNode(tsNode) ? tsNode : (tsNode as any).type : undefined; /* 1. if typeNode is provided, then find from tsTypeMap firstly */ if (typeNode) { const tsTypeRawName = this.getTsTypeRawName(typeNode, isReturnType); const parsedType = this.getTypeFromTSTypeMap( tsType, tsTypeRawName ? tsTypeRawName : '', ); if (parsedType) { return parsedType; } } /* 1. generate type by tsType */ const typeFlag = tsType.flags; let mask = ts.TypeFlags.Number; if (typeFlag & mask && !(~typeFlag & mask)) { res = builtinTypes.get('number'); } mask = ts.TypeFlags.NumberLiteral; if (!res && typeFlag & mask && !(~typeFlag & mask)) { res = builtinTypes.get('number'); } mask = ts.TypeFlags.String; if (!res && typeFlag & mask && !(~typeFlag & mask)) { res = builtinTypes.get('string'); } mask = ts.TypeFlags.StringLiteral; if (!res && typeFlag & mask && !(~typeFlag & mask)) { res = builtinTypes.get('string'); } mask = ts.TypeFlags.Boolean; if (!res && typeFlag & mask && !(~typeFlag & mask)) { res = builtinTypes.get('boolean'); } mask = ts.TypeFlags.BooleanLiteral; if (!res && typeFlag & mask && !(~typeFlag & mask)) { res = builtinTypes.get('boolean'); } mask = ts.TypeFlags.Void; if (!res && typeFlag & mask && !(~typeFlag & mask)) { res = builtinTypes.get('void'); } mask = ts.TypeFlags.Any; if (!res && typeFlag & mask && !(~typeFlag & mask)) { res = builtinTypes.get('any'); } mask = ts.TypeFlags.Undefined; if (!res && typeFlag & mask && !(~typeFlag & mask)) { res = builtinTypes.get('undefined'); } mask = ts.TypeFlags.Null; if (!res && typeFlag & mask && !(~typeFlag & mask)) { res = builtinTypes.get('null'); } mask = ts.TypeFlags.TypeParameter; if (!res && typeFlag & mask && !(~typeFlag & mask)) { if (tsType.symbol.declarations) { const type_name = tsType.symbol.getName(); const tp = tsType.symbol .declarations[0] as ts.TypeParameterDeclaration; const constraint_node = ts.getEffectiveConstraintOfTypeParameter(tp); let wide_type: Type = builtinTypes.get('generic')!; if (constraint_node) { // TypeNode const constraint_tstype = this.typechecker!.getTypeFromTypeNode(constraint_node); wide_type = this.tsTypeToType(constraint_tstype); } let default_type: Type | undefined = undefined; const default_node = tp.default; if (default_node) { const default_tstype = this.typechecker!.getTypeFromTypeNode(default_node); default_type = this.tsTypeToType(default_tstype); } const found = this.typeParameterStack.find( (t) => t.name == type_name && t.wideType.kind == wide_type.kind, ); if (found) return found; const type_param = new TSTypeParameter( type_name, wide_type, TypeResolver.typeParameterIndex++, default_type, ); this.typeParameterStack.push(type_param); return type_param; } } if (!res && tsType.symbol && tsType.symbol.declarations) { const typeDecl = tsType.symbol.declarations[0]; if (typeDecl.kind == ts.SyntaxKind.EnumDeclaration) { res = this.parseEnumType(typeDecl as ts.EnumDeclaration); } } if (!res && tsType.isUnion()) { res = this.parseUnionType(tsType, typeNode as ts.UnionTypeNode); } if (!res && this.isArray(tsType)) { if (!tsType.typeArguments) { throw new TypeError('array type has no type arguments'); } const elemType = this.tsTypeToType( tsType.typeArguments![0], typeNode ? (typeNode as any).elementType : undefined, ); res = new TSArray(elemType); } if ( !res && (this.isTypeReference(tsType) || this.isInterface(tsType) || this.isObjectLiteral(tsType) || this.isObjectType(tsType)) && tsType.symbol ) { const decls = tsType.symbol.declarations; if (decls) { const decl = tsType.symbol.declarations![0]; const type = this.symbolTypeMap.get(decl); if (!type) { throw new TypeError( `class/interface/object type not found, type name <${tsType.symbol.name}>. `, ); } // Type references (ObjectFlags.Reference) if ( /* The 'isTypeReference' function uses bit operators to determine whether the type is a Reference * But this result is not always correct */ this.isTypeReference(tsType) && tsType.objectFlags == ts.ObjectFlags.Reference ) { const typeArguments = this.typechecker!.getTypeArguments( tsType as ts.TypeReference, ).map((t) => { return this.tsTypeToType(t); }); res = processGenericType( type, typeArguments, (type as TSClass).typeArguments!, this.parserCtx, ); } else { res = type; } } else if (tsType.symbol.flags & ts.TypeFlags.Substitution) { /** TODO: symbol.declarations == undefined, means type always * has TypeFlags.Substitution flag?? */ res = new TSClass(); } else { throw new TypeError( `class/interface/object type contains neither declarations nor Substitution flag, type name <${tsType.symbol.name}>. `, ); } } if (!res && this.isFunction(tsType)) { const signature = tsType.getCallSignatures()[0]; res = this.parseSignature(signature); } if (!res && this.isTupleType(tsType)) { res = this.parseTupleType( tsType as ts.TupleType, typeNode as ts.TupleTypeNode, ); } if (!res) { Logger.warn(`Encounter un-processed type: ${tsType.flags}`); res = new Type(); } return res; } private parseTupleType( tsType: ts.TupleType, tupleTypeNode?: ts.TupleTypeNode, ) { const tupleType = new TSTuple(); if (tupleTypeNode && tupleTypeNode.elements) { for (const tupleElement of tupleTypeNode.elements) { const tupleElementType = this.generateNodeType(tupleElement); tupleType.addType(tupleElementType); } } else { if (!tsType.typeArguments) { throw new Error(`ts tuple type's typeArguments is undefined`); } for (const typeArg of tsType.typeArguments) { tupleType.addType(this.tsTypeToType(typeArg)); } } return tupleType; } private parseUnionType( tsUnionType: ts.UnionType, unionTypeNode?: ts.UnionTypeNode, ): Type { const union_type = new TSUnion(); /* 1. get type from typeNode firstly */ if (unionTypeNode && unionTypeNode.types) { for (const typeNode of unionTypeNode.types) { const type = this.generateNodeType(typeNode); union_type.addType(type); } } else { /* 2. get type from tsUnionType */ if (!tsUnionType.types) { return builtinTypes.get('any')!; } for (const tsType of tsUnionType.types) { union_type.addType(this.tsTypeToType(tsType)); } } const types = union_type.types; if (types.every((type) => type === types[0])) { return types[0]; } // T | null will be treated as nullable T type if (types.find((type) => type.kind === TypeKind.NULL)) { const nonNullTypes = types.filter( (type) => type.kind !== TypeKind.NULL, ); if ( nonNullTypes.length > 0 && nonNullTypes.every((type) => type === nonNullTypes[0]) && !nonNullTypes[0].isPrimitive ) { return nonNullTypes[0]; } } return union_type; } private parseEnumType(node: ts.EnumDeclaration): Type { const scope = this.currentScope!; let start = 0; const enumType = new TSEnum(node.name.getText()); for (const member of node.members) { const name = member.name.getText(); let value: number | string = start; if (member.initializer) { value = this.parseEnumMemberValue(enumType, member.initializer); if (member.initializer.kind == ts.SyntaxKind.NumericLiteral) { start = (value as number) + 1; } } else { start++; } enumType.addMember(name, value); } scope.addType(node.name.getText(), enumType); return enumType; } private parseEnumMemberValue( enumType: TSEnum, expr: ts.Expression, ): number | string { switch (expr.kind) { case ts.SyntaxKind.StringLiteral: return (expr as ts.StringLiteral).text; // return the string value without \' or \" case ts.SyntaxKind.NumericLiteral: return parseInt((expr as ts.NumericLiteral).getText()); case ts.SyntaxKind.Identifier: { const name = (expr as ts.Identifier).getText(); const value = enumType.getMember(name); if (!value) { throw new TypeError(`EnumMember cannot find ${name}`); } return value; } case ts.SyntaxKind.BinaryExpression: { const bnode = expr as ts.BinaryExpression; const left = this.parseEnumMemberValue(enumType, bnode.left); const right = this.parseEnumMemberValue(enumType, bnode.right); switch (bnode.operatorToken.kind) { case ts.SyntaxKind.PlusToken: if (typeof left == 'string' || typeof right == 'string') return `${left}${right}`; else return (left as number) + (right as number); default: throw new TypeError( `EnumMember cannot support the operator ${ ts.SyntaxKind[bnode.operatorToken.kind] }`, ); } } default: throw new TypeError( `EnumMember don't support dynamic expression`, ); } } private isObject(type: ts.Type): type is ts.ObjectType { return !!(type.flags & ts.TypeFlags.Object); } private isTypeReference(type: ts.Type): type is ts.TypeReference { return ( this.isObject(type) && !!(type.objectFlags & ts.ObjectFlags.Reference) ); } private isInterface(type: ts.Type): type is ts.InterfaceType { return ( this.isObject(type) && !!(type.objectFlags & ts.ObjectFlags.Interface) ); } private isObjectLiteral(type: ts.Type) { return ( this.isObject(type) && type.symbol && type.symbol.name === '__object' ); } // in most cases, the type has Anonymous ObjectTypeFlag private isObjectType(type: ts.Type) { return ( this.isObject(type) && type.symbol && type.symbol.name === '__type' && !this.isFunction(type) ); } private isTupleType(tsType: ts.Type) { if (this.isTypeReference(tsType)) { if ((tsType.target.objectFlags & ts.ObjectFlags.Tuple) !== 0) { return true; } } return false; } private isArray(type: ts.Type): type is ts.TypeReference { return ( this.isTypeReference(type) && type.symbol && type.symbol.name === 'Array' ); } private isFunction(type: ts.Type): type is ts.ObjectType { if (this.isObject(type)) { return type.getCallSignatures().length > 0; } return false; } private parseObjectType(node: ts.Node, tsClass: TSClass) { const cached_type = this.nodeTypeCache.get(node); /** for TypeLiteral, it might be putted into cache before parsing */ if (cached_type && this.parsedClassTypes.has(tsClass)) return cached_type; tsClass.isLiteral = true; this.nodeTypeCache.set(node, tsClass); if (ts.isTypeAliasDeclaration(node.parent)) { this.parseTypeParameters(tsClass, node.parent, this.currentScope); } const type = this.typechecker!.getTypeAtLocation(node); type.getProperties().map((prop) => { const propName = prop.name; const valueDecl = prop.valueDeclaration; if (!valueDecl) { throw new TypeError( `property ${propName} has no declaration when parsing object type`, ); } const tsType = this.generateNodeType(valueDecl); /* put all object literal's METHOD into vtable, FIELD into instance */ if ( tsType instanceof TSFunction && ts.isMethodDeclaration(valueDecl) ) { this.setMethod( valueDecl as ts.MethodDeclaration, null, tsClass, FunctionKind.METHOD, ); } else { tsClass.addMemberField({ name: propName, type: tsType, optional: (valueDecl as any).questionToken ? true : false, }); } }); this.parsedClassTypes.add(tsClass); return tsClass; } private parseSignature(signature: ts.Signature | undefined) { if (!signature) { throw new TypeError('signature is undefined'); } const decl = signature.getDeclaration(); const cached_type = this.nodeTypeCache.get(decl); if (cached_type) return cached_type as TSFunction; const tsFunction = new TSFunction(); /* parse modifiers */ tsFunction.isDeclare = signature.declaration ? this.parseNestDeclare( < | ts.FunctionLikeDeclaration | ts.ModuleDeclaration | ts.ClassDeclaration >signature.declaration, ) : false; tsFunction.isStatic = signature.declaration ? this.parseStatic( signature.declaration, ) : false; tsFunction.isBinaryenImpl = !!signature.declaration?.modifiers?.find( (modifier) => modifier.kind === ts.SyntaxKind.Decorator && (modifier).expression.getText() === BuiltinNames.decorator, ); tsFunction.isExport = !!signature.declaration?.modifiers?.find( (modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword, ); tsFunction.isMethod = tsFunction.isMethod || ts.isConstructorDeclaration(decl) || ts.isMethodDeclaration(decl) || ts.isConstructSignatureDeclaration(decl) || ts.isMethodSignature(decl) || ts.isAccessor(decl); /* parse original parameters type */ // TODO check currentScope is right this.parseTypeParameters( tsFunction, signature.getDeclaration(), this.currentScope && this.currentScope!.kind == ScopeKind.FunctionScope ? this.currentScope : null, ); signature.getParameters().map((param, index) => { const valueDecl = param.valueDeclaration!; if (ts.isParameter(valueDecl) && valueDecl.dotDotDotToken) { /* restParamIdx should include the @context and @this */ tsFunction.restParamIdx = index; } if (ts.isParameter(valueDecl) && valueDecl.questionToken) { tsFunction.addIsOptionalParam(true); } else { tsFunction.addIsOptionalParam(false); } const tsType = this.typechecker!.getTypeAtLocation(valueDecl); let customType = this.tsTypeToType(tsType, (valueDecl as any).type); // if this parameter type is generic, may need to deal with the generic chain if (isTypeGeneric(customType)) { //function and class if (customType instanceof TSTypeWithArguments) { let typeArguments: Type[] = []; // e.g. // type ItemGenerator = (item: T, index: U) => void // function test_func(func: ItemGenerator, a: T, b: U) {...} if (tsType.aliasTypeArguments) { typeArguments = tsType.aliasTypeArguments!.map((t) => { return this.tsTypeToType(t); }); } else if (this.isTypeReference(tsType)) { // e.g. // class Foo {...}; // function bar(a: Foo, data: Y[]) {...} typeArguments = this.typechecker!.getTypeArguments( tsType as ts.TypeReference, ).map((t) => { return this.tsTypeToType(t); }); } if (customType.typeArguments) customType = processGenericType( customType, typeArguments, customType.typeArguments, this.parserCtx, ); } else { //TODO } } tsFunction.addParamType(customType); }); /* parse return type */ const returnType = signature.getReturnType(); const customType = this.tsTypeToType(returnType, decl.type, true); tsFunction.returnType = customType; this.nodeTypeCache.set(decl, tsFunction); return tsFunction; } private parseNestDeclare( node: | ts.FunctionLikeDeclaration | ts.ModuleDeclaration | ts.ClassDeclaration | ts.InterfaceDeclaration, ): boolean { let res = false; if (node.modifiers) { const hasDeclareKeyword = node.modifiers.find((modifier) => { return modifier.kind === ts.SyntaxKind.DeclareKeyword; }); if (hasDeclareKeyword) { return true; } } if (node.parent.kind === ts.SyntaxKind.ModuleBlock) { res = this.parseNestDeclare( node.parent.parent, ); } else if (node.parent.kind === ts.SyntaxKind.ClassDeclaration) { res = this.parseNestDeclare(node.parent); } return res; } private parseIndexSignature( infc: TSInterface, indexSignature: ts.IndexSignatureDeclaration, ) { const param_type = indexSignature.parameters[0]; const key_type = this.tsTypeToType( this.typechecker!.getTypeFromTypeNode(param_type.type!), ); const value_type = this.tsTypeToType( this.typechecker!.getTypeFromTypeNode(indexSignature.type), ); if ( key_type.kind !== TypeKind.NUMBER && key_type.kind !== TypeKind.STRING ) { throw new TypeError( `${infc.className} indexSignature need number or string : ${key_type}`, ); } if (key_type.kind === TypeKind.NUMBER) { infc.setNumberIndexType(value_type); } else { infc.setStringIndexType(value_type); } Logger.debug( `=== ${infc.className} index type [${key_type}] : ${value_type}`, ); } private parseInfcType(node: ts.InterfaceDeclaration, infc: TSInterface) { if (this.parsedClassTypes.has(infc)) return infc; this.nodeTypeCache.set(node, infc); infc.setClassName(node.name!.getText()); // if (node.typeParameters) { // this.parseTypeParameterIndex(node.typeParameters, infc); // } this.parseTypeParameters(infc, node, this.currentScope); infc.isDeclare = this.parseNestDeclare(node); const heritages = node.heritageClauses; const baseClassType: TSClass | null = null; let baseInfcType: TSInterface | null = null; const typeArguments = new Array(); /** if extends more than two classes, an error will be thrown, * if extends a class, implements some interface, the subclass is subtype of supclass, * but do not guarantee that it will be a subtype of the interface either. * if implements more than one interface, the subclass is subtype of the first interface. */ if (heritages) { const heritageTypes: TSClass[] = []; for (const h of heritages) { for (const type of h.types) { const tsType = this.typechecker!.getTypeAtLocation(type); const symbol = tsType.symbol; if (!symbol) { break; } const baseDecl = symbol.declarations![0]; const baseType = this.symbolTypeMap.get(baseDecl); if (type.typeArguments) { type.typeArguments.forEach((t) => { typeArguments.push( this.generateNodeType(t) as TSTypeParameter, ); }); } if (!this.parsedClassTypes.has(baseType as TSClass)) { if (baseType instanceof TSInterface) { this.parseInfcType( baseDecl as ts.InterfaceDeclaration, baseType, ); } else if (baseType instanceof TSClass) { this.parseClassType( baseDecl as ts.ClassDeclaration, baseType, ); } } heritageTypes.push(baseType as TSClass); } } for (const h of heritageTypes) { if (h instanceof TSInterface) { if (baseInfcType) { throw new TypeError( `unimpl interface ${infc.className} extends more than one interface`, ); } if (!baseInfcType) { baseInfcType = h; infc.setBase(baseInfcType); } } else { // TODO:interface extends class. Don’t implement it now throw new Error('unimpl interface extends class'); } } } if ( baseInfcType && isTypeGeneric(baseInfcType) && typeArguments.length > 0 ) { // For the same interface base on different inheritance chains, we will regenerate a new base infc type. // The purpose is to prevent these base types from affecting each other. // e.g. // interface IGeneric {...}; // interface IGenericBase1 extends IGeneric {...}; // interface IGenericBase2 extends IGeneric {...}; baseInfcType = processGenericType( baseInfcType, typeArguments, baseInfcType.typeArguments!, this.parserCtx, ) as TSInterface; infc.setBase(baseInfcType); baseInfcType.overrideOrOwnMethods.forEach((t) => { infc.overrideOrOwnMethods.add(t); }); } if (baseInfcType) { // TODO try resolve the template type for (const field of baseInfcType.fields) { infc.addMemberField(field); } for (const method of baseInfcType.memberFuncs) { infc.addMethod({ name: method.name, type: method.type.clone(), optional: method.optional, }); } } for (let i = 0; i < node.members.length; ++i) { const member = node.members[i]; if (member.kind == ts.SyntaxKind.IndexSignature) { this.parseIndexSignature( infc, member as ts.IndexSignatureDeclaration, ); continue; } /** Currently, we only handle PropertySignature and MethodSignature */ if ( member.kind !== ts.SyntaxKind.ConstructSignature && member.kind !== ts.SyntaxKind.PropertySignature && member.kind !== ts.SyntaxKind.MethodSignature && member.kind !== ts.SyntaxKind.CallSignature && member.kind !== ts.SyntaxKind.GetAccessor && member.kind !== ts.SyntaxKind.SetAccessor ) { continue; } let fieldType = this.generateNodeType(member); if ( member.kind === ts.SyntaxKind.CallSignature && node.members.length == 1 ) { return fieldType; } else if (member.kind === ts.SyntaxKind.CallSignature) { continue; } const funcKind = member.kind == ts.SyntaxKind.ConstructSignature ? FunctionKind.CONSTRUCTOR : FunctionKind.METHOD; const fieldName = funcKind == FunctionKind.CONSTRUCTOR ? 'constructor' : member.name!.getText(); if (fieldType instanceof TSUnion && member.questionToken) { const type = fieldType.types.find((type) => { return type instanceof TSFunction; }); if (type) { fieldType = type; } } if (fieldType instanceof TSFunction) { if (ts.isSetAccessor(member)) { fieldType.funcKind = FunctionKind.SETTER; } else if (ts.isGetAccessor(member)) { fieldType.funcKind = FunctionKind.GETTER; } else { fieldType.funcKind = funcKind; } infc.addMethod({ name: fieldName, type: fieldType, optional: member.questionToken ? true : false, }); this.parseTypeParameters( fieldType as TSFunction, member as ts.DeclarationWithTypeParameters, null, ); infc.overrideOrOwnMethods.add(fieldName); } else { infc.addMemberField({ name: fieldName, type: fieldType, optional: member.questionToken ? true : false, }); } } this.parsedClassTypes.add(infc); return infc; } private parseClassType(node: ts.ClassDeclaration, classType: TSClass) { if (this.parsedClassTypes.has(classType)) return classType; this.nodeTypeCache.set(node, classType); classType.setClassName(node.name!.getText()); const scope = this.parserCtx.nodeScopeMap.get(node)!; this.parseTypeParameters(classType, node, scope); classType.isDeclare = this.parseNestDeclare(node); const heritages = node.heritageClauses; let baseClassType: TSClass | null = null; let baseInfcType: TSInterface | null = null; const typeArguments = new Array(); /** if extends more than two classes, an error will be thrown, * if extends a class, implements some interface, the subclass is subtype of supclass, * but do not guarantee that it will be a subtype of the interface either. * if implements more than one interface, the subclass is subtype of the first interface. */ if (heritages) { const heritageTypes: TSClass[] = []; for (const h of heritages) { for (const type of h.types) { const baseTsType = this.typechecker!.getTypeAtLocation( type.expression, ); const symbol = baseTsType.symbol; const baseDecl = symbol.declarations![0]; const baseType = this.symbolTypeMap.get(baseDecl); if (type.typeArguments) { type.typeArguments.forEach((t) => { typeArguments.push( this.generateNodeType(t) as TSTypeParameter, ); }); } if (!this.parsedClassTypes.has(baseType as TSClass)) { if (baseType instanceof TSInterface) { this.parseInfcType( baseDecl as ts.InterfaceDeclaration, baseType, ); } else if (baseType instanceof TSClass) { this.parseClassType( baseDecl as ts.ClassDeclaration, baseType, ); } } heritageTypes.push(baseType as TSClass); } } for (const h of heritageTypes) { if (h instanceof TSInterface) { if (!baseClassType && !baseInfcType) { baseInfcType = h; classType.setImplInfc(baseInfcType); } } else { if (baseInfcType) { baseInfcType = null; classType.setImplInfc(baseInfcType); } if (baseClassType) { throw new TypeError('unimpl multiple base classes'); } baseClassType = h; classType.setBase(baseClassType); baseClassType.setDrivedClass(classType); } } } if ( baseInfcType && isTypeGeneric(baseInfcType) && typeArguments.length > 0 ) { baseInfcType = processGenericType( baseInfcType, typeArguments, baseInfcType.typeArguments!, this.parserCtx, ) as TSInterface; classType.setImplInfc(baseInfcType); let _baseInfcType: TSClass | null = baseInfcType; while (_baseInfcType) { this.currentScope!.parent!.addType( _baseInfcType.className, _baseInfcType, ); _baseInfcType = _baseInfcType.getBase(); } } // handling type parameters in the inheritance chain /* e.g. * class Generic {...}; * class GenericBase extends Generic {...}; */ // On the inheritance chain, the type parameter of Generic is Y, // and in its class definition, the type parameter is X. // So it needs to be corrected based on the actual type parameters on the inheritance chain. if ( baseClassType && isTypeGeneric(baseClassType) && typeArguments.length > 0 ) { // For the same class base on different inheritance chains, we will regenerate a new base class type. // The purpose is to prevent these base types from affecting each other. // e.g. // class Generic {...}; // class GenericBase1 extends Generic {...}; // class GenericBase2 extends Generic {...}; baseClassType = processGenericType( baseClassType, typeArguments, baseClassType.typeArguments!, this.parserCtx, classType.className, ) as TSClass; classType.setBase(baseClassType); baseClassType.setDrivedClass(classType); // create base generic class scope const geneicOwner = baseClassType.genericOwner ? baseClassType.genericOwner : baseClassType; if (geneicOwner.belongedScope) { createScopeBySpecializedType( baseClassType, geneicOwner.belongedScope.parent!, this.parserCtx, ); } } if (baseClassType) { // TODO try resolve the template type for (const field of baseClassType.fields) { classType.addMemberField(field); } for (const pair of baseClassType.staticFieldsInitValueMap) { classType.staticFieldsInitValueMap.set(pair[0], pair[1]); } for (const staticField of baseClassType.staticFields) { classType.addStaticMemberField(staticField); } for (const method of baseClassType.memberFuncs) { classType.addMethod({ name: method.name, type: method.type.clone(), optional: method.optional, }); } } // 1. parse constructor let ctorScope: FunctionScope; const constructor = node.members.find((member) => { return ts.isConstructorDeclaration(member); }); if (constructor) { ctorScope = this.nodeScopeMap.get(constructor)! as FunctionScope; } else { const classScope = this.nodeScopeMap.get(node)! as ClassScope; ctorScope = new FunctionScope(classScope); ctorScope.setFuncName('constructor'); ctorScope.setClassName(node.name!.getText()); ctorScope.addVariable(new Variable('this', classType)); const ctorType = new TSFunction(FunctionKind.CONSTRUCTOR); ctorType.belongedClass = classType; ctorType.setBelongedScope(ctorScope); ctorType.returnType = classType; ctorType.isMethod = true; classType.hasDeclareCtor = false; ctorScope.setFuncType(ctorType); classType.ctorType = ctorType; if (baseClassType) { const baseCtorType = baseClassType.ctorType; const paramTypes = baseCtorType.getParamTypes(); for (let i = 0; i < paramTypes.length; i++) { const paramType = paramTypes[i]; const newType = processGenericType( paramType, typeArguments, classType.typeArguments!, this.parserCtx, ); if (!newType) throw new TypeError('unimpl TSTypeParameter exchange'); ctorType.addParamType(paramTypes[i]); ctorScope.addParameter( new Parameter(`@anonymous${i}`, paramTypes[i]), ); } } } // 2. parse other fields for (const member of node.members) { if (ts.isSemicolonClassElement(member)) { /* ES6 allows Semicolon as class elements, we just skip them */ continue; } const name = ts.isConstructorDeclaration(member) ? 'constructor' : member.name!.getText(); if (ts.isPropertyDeclaration(member)) { const type = this.generateNodeType(member); const modifier = member.modifiers?.find((m) => { return m.kind === ts.SyntaxKind.ReadonlyKeyword; }) ? 'readonly' : undefined; const staticModifier = member.modifiers?.find((m) => { return m.kind === ts.SyntaxKind.StaticKeyword; }) ? 'static' : undefined; const classField: TsClassField = { name: name, type: type, modifier: modifier, visibility: 'public', static: staticModifier, optional: member.questionToken ? true : false, }; if (member.initializer) { if (classField.static) { let index = classType.getStaticFieldIndex(name); if (index === -1) { index = classType.staticFields.length; } classType.staticFieldsInitValueMap.set( index, this.parserCtx.expressionProcessor.visitNode( member.initializer, ), ); } else { ctorScope.addStatement( this.parserCtx.statementProcessor.createFieldAssignStmt( member.initializer, classType, type, name, ), ); } } if (!classField.static) { const found = classType.getMemberField(name); if (found) continue; classType.addMemberField(classField); } else { const found = classType.getStaticMemberField(name); if (found) continue; classType.addStaticMemberField(classField); } } if (ts.isSetAccessor(member)) { this.setMethod( member, baseClassType, classType, FunctionKind.SETTER, ); } if (ts.isGetAccessor(member)) { this.setMethod( member, baseClassType, classType, FunctionKind.GETTER, ); } if (ts.isMethodDeclaration(member)) { const kind = member.modifiers?.find((m) => { return m.kind === ts.SyntaxKind.StaticKeyword; }) ? FunctionKind.STATIC : FunctionKind.METHOD; this.setMethod(member, baseClassType, classType, kind); } if (ts.isConstructorDeclaration(member)) { const ctorType = this.parseConstructor(member); ctorScope.setFuncType(ctorType); ctorType.setBelongedScope(ctorScope); classType.ctorType = ctorType; ctorType.belongedClass = classType; } } /** Reset baseInfcType if the base interface contains optional properties, which the class doesn't have */ if (baseInfcType) { for (const field of baseInfcType.fields) { if (field.optional) { const found = classType.getMemberField(field.name); if (!found || !found.optional) { baseInfcType = null; classType.setImplInfc(baseInfcType); break; } } } if (baseInfcType) { for (const method of baseInfcType.memberFuncs) { if (method.optional) { const found = classType.getMethod( method.name, method.type.funcKind, ); if (found.index === -1 || found.method!.optional) { baseInfcType = null; classType.setImplInfc(baseInfcType); break; } } } } } /** reorder member orders for optimization */ if (baseInfcType) { const baseFields = baseInfcType.fields; for (let i = baseFields.length - 1; i >= 0; i--) { const index = classType.fields.findIndex( (field) => field.name == baseFields[i].name, ); if (index > -1) { const targetField = classType.fields[index]; classType.fields.splice(index, 1); classType.fields.unshift(targetField); } } const baseMethods = baseInfcType.memberFuncs; for (let i = baseMethods.length - 1; i >= 0; i--) { const index = classType.getMethod( baseMethods[i].name, baseMethods[i].type.funcKind, ).index; if (index > -1) { const targetMethod = classType.memberFuncs[index]; classType.memberFuncs.splice(index, 1); classType.memberFuncs.unshift(targetMethod); } } } this.parsedClassTypes.add(classType); return classType; } private setMethod( decl: | ts.AccessorDeclaration | ts.MethodDeclaration | ts.PropertyAssignment, baseClassType: TSClass | null, classType: TSClass, funcKind: FunctionKind, ) { const methodName = decl.name.getText(); let func: | ts.AccessorDeclaration | ts.MethodDeclaration | ts.FunctionExpression; if (ts.isPropertyAssignment(decl)) { func = decl.initializer as ts.FunctionExpression; } else { func = decl; } const type = this.generateNodeType(func) as TSFunction; let tsFuncType = new TSFunction(funcKind); const scope = this.parserCtx.nodeScopeMap.get(func)!; this.parseTypeParameters(tsFuncType, func, scope); const nameWithPrefix = getMethodPrefix(funcKind) + decl.name.getText(); type.funcKind = funcKind; tsFuncType = type; let isOverride = false; if (baseClassType) { const baseFuncType = baseClassType.getMethod(methodName, funcKind) .method?.type; if (baseFuncType) { if (!tsFuncType.belongedClass) tsFuncType.belongedClass = classType; // override the method of base class classType.updateMethod(methodName, funcKind, tsFuncType); isOverride = true; } } if (!isOverride) { /* override methods has been copied from base class, only add non-override methods here */ classType.addMethod({ name: methodName, type: tsFuncType, optional: func.questionToken ? true : false, }); } const funcDef = this.parserCtx.getScopeByNode(func); if (funcDef && funcDef instanceof FunctionScope) { funcDef.setFuncType(tsFuncType); tsFuncType.setBelongedScope(funcDef); } classType.overrideOrOwnMethods.add(nameWithPrefix); // the accessor of the sub class are placed in front of the accessor of the base class for optimization. const setterIndex = classType.getMethod( methodName, FunctionKind.SETTER, ).index; const isSetterOverrideOrOwn = classType.overrideOrOwnMethods.has( `${'set_'}${methodName}`, ); const getterIndex = classType.getMethod( methodName, FunctionKind.GETTER, ).index; const isGetterOverrideOrOwn = classType.overrideOrOwnMethods.has( `${'get_'}${methodName}`, ); if (setterIndex > -1 && getterIndex > -1) { if (!isSetterOverrideOrOwn && setterIndex < getterIndex) { [ classType.memberFuncs[setterIndex], classType.memberFuncs[getterIndex], ] = [ classType.memberFuncs[getterIndex], classType.memberFuncs[setterIndex], ]; } else if (!isGetterOverrideOrOwn && getterIndex < setterIndex) { [ classType.memberFuncs[getterIndex], classType.memberFuncs[setterIndex], ] = [ classType.memberFuncs[setterIndex], classType.memberFuncs[getterIndex], ]; } } } parseTypeParameters( tstype: TSTypeWithArguments, node: ts.DeclarationWithTypeParameters, scope: Scope | null, ) { const typeParams = ts.getEffectiveTypeParameterDeclarations(node); if (!typeParams) return; for (const tp of typeParams!) { const name = tp.name.getText(); const constraint_node = ts.getEffectiveConstraintOfTypeParameter(tp); let wide_type: Type = builtinTypes.get('generic')!; if (constraint_node) { // TypeNode const constraint_tstype = this.typechecker!.getTypeFromTypeNode(constraint_node); wide_type = this.tsTypeToType(constraint_tstype); } let default_type: Type | undefined = undefined; const default_node = tp.default; if (default_node) { const default_tstype = this.typechecker!.getTypeFromTypeNode(default_node); default_type = this.tsTypeToType(default_tstype); } let type_param: TSTypeParameter | undefined; type_param = this.typeParameterStack.find( (t) => t.name == name && t.wideType.kind == wide_type.kind, ); if (!type_param) { type_param = new TSTypeParameter( name, wide_type, TypeResolver.typeParameterIndex++, default_type, ); this.typeParameterStack.push(type_param); } tstype.addTypeParameter(type_param); if (scope) { scope.addType(type_param.name, type_param); } } } private parseStatic(node: ts.FunctionLikeDeclaration): boolean { let res = false; if (node.modifiers) { const hasStaticKeyword = node.modifiers.find((modifier) => { return modifier.kind === ts.SyntaxKind.StaticKeyword; }); if (hasStaticKeyword) { res = true; } } return res; } private parseConstructor(ctor: ts.ConstructorDeclaration) { const signature = this.typechecker!.getSignatureFromDeclaration(ctor); const tsFunction = this.parseSignature(signature); tsFunction.funcKind = FunctionKind.CONSTRUCTOR; return tsFunction; } private parseTypeRefRelationship(type: TSClass) { if (!this.typeRefsMap.has(type)) { this.typeRefsMap.set(type, new Set()); } const refs = this.typeRefsMap.get(type)!; type.fields.map((field) => { if (!field.static) { this.parseTypeRefRelationship2(field.type, refs); } }); type.memberFuncs.map((method) => { if (!method.type.isStatic) { this.parseTypeRefRelationship2(method.type, refs); } }); } private parseTypeRefRelationship2(type: Type, refsSet: Set) { if (type instanceof TSClass) { refsSet.add(type); return; } if (type instanceof TSArray) { this.parseTypeRefRelationship2(type.elementType, refsSet); } if (type instanceof TSFunction) { this.parseTypeRefRelationship2(type.returnType, refsSet); for (const param of type.getParamTypes()) { this.parseTypeRefRelationship2(param, refsSet); } } } private typeIdAllocate(type: TSClass) { if (type.traverseStatus === TraverseStatus.PROCESSED) { return; } // meet a circular reference if (type.traverseStatus === TraverseStatus.VISITTED) { type.traverseStatus = TraverseStatus.PROCESSED; type.typeId = this.parserCtx.typeId; if (type.className === '') { type.setClassName(`@object_type${type.typeId}`); } this.parserCtx.typeId += 2; this.loopEntry = type; this.parserCtx.recGroupTypes.push(new Array()); return; } type.traverseStatus = TraverseStatus.VISITTED; const refArray = this.typeRefsMap.get(type); if (refArray && refArray.size > 0) { refArray.forEach((ref) => { this.typeIdAllocate(ref); }); } if (type.traverseStatus === TraverseStatus.VISITTED) { const shapeStr = this.getShapeDesc(type); type.typeId = this.generateTypeId(shapeStr); if (type.className === '') { type.setClassName(`@object_type${type.typeId}`); } type.traverseStatus = TraverseStatus.PROCESSED; } if (this.loopEntry) { const len = this.parserCtx.recGroupTypes.length; this.parserCtx.recGroupTypes[len - 1].push(type); if (type === this.loopEntry) { this.loopEntry = null; } } } private generateTypeId(typeString: string): number { if (this.parserCtx.typeIdMap.has(typeString)) { return this.parserCtx.typeIdMap.get(typeString)!; } const id = this.parserCtx.typeId; this.parserCtx.typeId += 2; // next typeid this.parserCtx.typeIdMap.set(typeString, id); return id; } private getShapeDesc(tsClass: TSClass) { let str = ''; tsClass.fields.map((field) => { str = str + field.name + (field.optional ? '?: ' : ': ') + this.getTypeString(field.type) + ','; }); tsClass.memberFuncs .filter((func) => { return func.type.funcKind !== FunctionKind.STATIC; }) .map( (func) => (str = str + func.name + (func.optional ? '?: ' : ': ') + this.getTypeString(func.type) + '(m)' + ','), ); /* We add a (m) suffix to indicate this is a method rather than a field of function type */ return str; } private noTypeParmeters(type: readonly ts.Type[]) { return ( type.length > 0 && type.every((t) => { return !(t.flags & ts.TypeFlags.TypeParameter); }) ); } private getTypeString(type: Type): string { if (type instanceof Primitive) { return type.toString(); } else if (type instanceof TSUnion) { return type._types.join('|'); } else if (type instanceof TSArray) { return this.getTypeString(type.elementType) + '[]'; } else if (type instanceof TSClass) { return type.typeId.toString(); } else if (type instanceof WasmType) { return type.getName(); } else if (type instanceof TSFunction) { let res = '('; const len = type.getParamTypes().length; const paramTypes: string[] = new Array(len); for (let i = 0; i < len - 1; i++) { paramTypes[i] = this.getTypeString(type.getParamTypes()[i]); if (type.isOptionalParams[i]) { paramTypes[i] = '?' + paramTypes[i]; } } if (type.hasRest()) { paramTypes[len - 1] = '...' + this.getTypeString(type.getParamTypes()[len - 1]); } res = res + paramTypes.join(',') + ')'; res = res + '=>' + this.getTypeString(type.returnType); return res; } else { // types unimplemented Logger.info(`types unimplemented ${type.kind}`); return 'unknown'; } } public static maybeBuiltinWasmType(node: ts.Node) { const definedTypeName = (node as any).type?.typeName?.escapedText; if (definedTypeName) { if (builtinWasmTypes.has(definedTypeName)) { return builtinWasmTypes.get(definedTypeName)!; } } } public arrayTypeCheck(node: ts.Node): boolean { const parentNode = node.parent; if ( ts.isVariableDeclaration(parentNode) || (ts.isBinaryExpression(parentNode) && parentNode.operatorToken.kind === ts.SyntaxKind.EqualsToken) ) { const type = this.typechecker!.getTypeAtLocation(parentNode); if (this.isArray(type) && type.typeArguments) { return true; } } return false; } public static findTypeInSpecialiezedTypeCache( genericType: TSTypeWithArguments, signature: string, ) { const genericOwner = genericType.genericOwner ? genericType.genericOwner : genericType; const typeSignature = genericType instanceof TSClass ? genericType.className + signature : signature; const cache = TypeResolver.specializedTypeCache.get(genericOwner); let found: Type | undefined; if (cache) { cache.forEach((v) => { if (v.has(typeSignature)) { found = v.get(typeSignature); } }); } if (found) return found; } public static updateSpecializedTypeCache( genericType: TSTypeWithArguments, typeSignature: string, specializedType: Type, ) { if (TypeResolver.specializedTypeCache.has(genericType)) { const value = new Map(); value.set(typeSignature, specializedType); TypeResolver.specializedTypeCache.get(genericType)!.push(value); } else { const value = new Map(); value.set(typeSignature, specializedType); TypeResolver.specializedTypeCache.set(genericType, [value]); } } } export class CustomTypeResolver { globalScopes: Array; constructor(private parserCtx: ParserContext) { this.globalScopes = this.parserCtx.globalScopes; } visit() { for (const globalScope of this.globalScopes) { globalScope.traverseScopTree((scope) => { if ( scope instanceof FunctionScope || scope instanceof GlobalScope ) { /* Assign index for function variables */ scope.initVariableIndex(); scope.initParamIndex(); } }); this.parseThis(globalScope); this.parseContext(globalScope); } } private parseThis(scope: Scope) { if ( scope instanceof FunctionScope && scope.parent instanceof ClassScope && scope.isMethod() && !scope.isStatic() ) { const thisType: Type = scope.parent.classType; for (const variable of scope.varArray) { if (variable.varName === 'this') { variable.varType = thisType; break; } } } /* traverse scope's children */ for (const child of scope.children) { this.parseThis(child); } } private parseContext(scope: Scope) { if (scope instanceof ClosureEnvironment) { const currentCtxVar = scope.contextVariable!; let parentScope = scope.parent; let parentCtxVar: Variable | undefined = undefined; // skip class scope while ( parentScope instanceof ClassScope || parentScope instanceof NamespaceScope ) { parentScope = parentScope.parent; } if (scope instanceof FunctionScope) { /* function scope: parse param context type and variable context type */ if (parentScope instanceof GlobalScope) { parentCtxVar = undefined; } else if (parentScope instanceof ClosureEnvironment) { parentCtxVar = parentScope.contextVariable!; } this.parseParamContextType(scope, parentCtxVar); const realParamCtxVar = new Parameter( '@context', scope.realParamCtxType, [], 0, ); this.parseVarContextType( scope, currentCtxVar, realParamCtxVar.varType as TSContext, realParamCtxVar, ); } else if ( scope instanceof BlockScope && scope.parent?.getNearestFunctionScope() ) { /* block scope: parse variable context type */ if (parentScope instanceof GlobalScope) { parentCtxVar = undefined; } else if (parentScope instanceof ClosureEnvironment) { parentCtxVar = parentScope.contextVariable; } this.parseVarContextType( scope, currentCtxVar, parentCtxVar ? (parentCtxVar.varType as TSContext) : new TSContext(), parentCtxVar, ); } } /* traverse scope's children */ for (const child of scope.children) { this.parseContext(child); } } private parseParamContextType( scope: FunctionScope, parentVarContextVar?: Variable, ) { let paramContextType = new TSContext(); if (parentVarContextVar) { paramContextType = new TSContext( parentVarContextVar!.varType as TSContext, ); } /* record the realType */ scope.realParamCtxType = paramContextType; scope.addType(paramContextType.toString(), paramContextType); } private parseVarContextType( scope: ClosureEnvironment, currentCtxVar: Variable, parentContextType: TSContext, parentCtxVar?: Variable, ) { if (parentCtxVar) { currentCtxVar.initContext = parentCtxVar; } const varFreeVarTypeList: Type[] = []; if (scope instanceof FunctionScope) { scope.paramArray.forEach((value) => { if (value.varIsClosure) { value.belongCtx = currentCtxVar; value.closureIndex = varFreeVarTypeList.length; varFreeVarTypeList.push(value.varType); } }); } scope.varArray.forEach((value) => { if (value.varIsClosure) { value.belongCtx = currentCtxVar; value.closureIndex = varFreeVarTypeList.length; varFreeVarTypeList.push(value.varType); } }); const varContextType = new TSContext( parentContextType, varFreeVarTypeList, ); currentCtxVar.varType = varContextType; scope.addType(varContextType.toString(), varContextType); return varContextType; } } ================================================ FILE: src/utils.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import ts from 'typescript'; import path from 'path'; import { BlockScope, ClassScope, FunctionScope, GlobalScope, NamespaceScope, Scope, ScopeKind, } from './scope.js'; import ExpressionProcessor, { Expression, IdentifierExpression, } from './expression.js'; import { BuiltinNames } from '../lib/builtin/builtin_name.js'; import { ParserContext } from './frontend.js'; import { FunctionKind, Type, TSInterface, TypeKind, TSClass, TSTypeParameter, TSFunction, TSArray, TSUnion, builtinTypes, builtinWasmTypes, TypeResolver, TSTypeWithArguments, WasmArrayType, WasmStructType, TSTuple, } from './type.js'; import { UnimplementError } from './error.js'; import { Statement } from './statement.js'; import { Logger } from './log.js'; export interface importGlobalInfo { internalName: string; externalModuleName: string; externalBaseName: string; globalType: Type; } export interface importFunctionInfo { internalName: string; externalModuleName: string; externalBaseName: string; funcType: Type; } export enum MatchKind { ExactMatch, ToAnyMatch, FromAnyMatch, ClassMatch, ClassInheritMatch, ClassInfcMatch, ToArrayAnyMatch, FromArrayAnyMatch, MisMatch, } export enum CommentKind { NativeSignature = 'NativeSignature', Import = 'Import', Export = 'Export', WASMArray = 'WASMArray', WASMStruct = 'WASMStruct', } export interface NativeSignature { paramTypes: Type[]; returnType: Type; } export interface Import { moduleName: string; funcName: string; } export interface Export { exportName: string; } export enum PackedTypeKind { Not_Packed = 'Not_Packed', I8 = 'I8', I16 = 'I16', } export enum MutabilityKind { Immutable = 'Immutable', Mutable = 'Mutable', } export enum NullabilityKind { NonNullable = 'NonNullable', Nullable = 'Nullable', } export interface WASMArray { WASMArray: boolean; packedType: PackedTypeKind; mutability: MutabilityKind; nullability: NullabilityKind; } export interface WASMStruct { WASMStruct: boolean; packedTypes?: PackedTypeKind[]; mutabilitys?: MutabilityKind[]; nullability?: NullabilityKind; baseTypeName?: string; } export class Stack { private items: T[] = []; push(item: T) { this.items.push(item); } pop() { if (this.isEmpty()) { throw new Error('Current stack is empty, can not pop'); } return this.items.pop()!; } peek() { if (this.isEmpty()) { throw new Error('Current stack is empty, can not get peek item'); } return this.items[this.items.length - 1]; } isEmpty() { return this.items.length === 0; } clear() { this.items = []; } size() { return this.items.length; } getItemAtIdx(index: number) { if (index >= this.items.length) { throw new Error('index is greater than the size of the stack'); } return this.items[index]; } } export function getCurScope( node: ts.Node, nodeScopeMap: Map, ): Scope | null { if (!node) return null; const scope = nodeScopeMap.get(node); if (scope) return scope; return getCurScope(node.parent, nodeScopeMap); } export function getNearestFunctionScopeFromCurrent(currentScope: Scope | null) { if (!currentScope) { throw new Error('current scope is null'); } const functionScope = currentScope.getNearestFunctionScope(); if (!functionScope) { return null; } return functionScope; } export function generateNodeExpression( exprCompiler: ExpressionProcessor, node: ts.Node, ): Expression { return exprCompiler.visitNode(node); } export function parentIsFunctionLike(node: ts.Node) { if ( node.parent.kind === ts.SyntaxKind.FunctionDeclaration || node.parent.kind === ts.SyntaxKind.MethodDeclaration || node.parent.kind === ts.SyntaxKind.SetAccessor || node.parent.kind === ts.SyntaxKind.GetAccessor || node.parent.kind === ts.SyntaxKind.FunctionExpression || node.parent.kind === ts.SyntaxKind.ArrowFunction || node.parent.kind === ts.SyntaxKind.Constructor ) { return true; } return false; } export function isScopeNode(node: ts.Node) { if ( node.kind === ts.SyntaxKind.SourceFile || node.kind === ts.SyntaxKind.ModuleDeclaration || node.kind === ts.SyntaxKind.FunctionDeclaration || node.kind === ts.SyntaxKind.FunctionExpression || node.kind === ts.SyntaxKind.ArrowFunction || node.kind === ts.SyntaxKind.ClassDeclaration || node.kind === ts.SyntaxKind.SetAccessor || node.kind === ts.SyntaxKind.GetAccessor || node.kind === ts.SyntaxKind.Constructor || node.kind === ts.SyntaxKind.MethodDeclaration || node.kind === ts.SyntaxKind.ForStatement || node.kind === ts.SyntaxKind.ForOfStatement || node.kind === ts.SyntaxKind.WhileStatement || node.kind === ts.SyntaxKind.DoStatement || node.kind === ts.SyntaxKind.CaseClause || node.kind === ts.SyntaxKind.DefaultClause ) { return true; } if (node.kind === ts.SyntaxKind.Block && !parentIsFunctionLike(node)) { return true; } return false; } export function mangling( scopeArray: Array, delimiter = BuiltinNames.moduleDelimiter, prefixStack: Array = [], ) { scopeArray.forEach((scope) => { const currName = convertWindowsPath(scope.getName()); if (scope instanceof GlobalScope) { scope.startFuncName = `${currName}|start`; prefixStack.push(currName); scope.varArray.forEach((v) => { v.mangledName = `${prefixStack.join(delimiter)}|${v.varName}`; }); scope.namedTypeMap.forEach((t, _) => { if (t.kind == TypeKind.INTERFACE) { const infc = t as TSInterface; if (infc.mangledName == '') { infc.mangledName = `${prefixStack.join(delimiter)}|${ infc.className }`; } } }); } else if (scope instanceof NamespaceScope) { prefixStack.push(currName); scope.varArray.forEach((v) => { v.mangledName = `${prefixStack.join(delimiter)}|${v.varName}`; }); scope.namedTypeMap.forEach((t, _) => { if (t.kind == TypeKind.INTERFACE) { const infc = t as TSInterface; infc.mangledName = `${prefixStack.join(delimiter)}|${ infc.className }`; } }); } else if (scope instanceof FunctionScope) { prefixStack.push(currName); } else if (scope instanceof ClassScope) { prefixStack.push(currName); scope.classType.mangledName = `${prefixStack.join(delimiter)}`; } else if (scope instanceof BlockScope) { prefixStack.push(currName); } scope.mangledName = `${prefixStack.join(delimiter)}`; mangling(scope.children, delimiter, prefixStack); prefixStack.pop(); }); } export function getModulePath( declaration: ts.ImportDeclaration | ts.ExportDeclaration, currentGlobalScope: GlobalScope, ) { /* moduleSpecifier contains quotation marks, so we must slice them to get real module name */ if (declaration.moduleSpecifier === undefined) return undefined; const moduleSpecifier = declaration .moduleSpecifier!.getText() .slice("'".length, -"'".length); const currentModuleName = currentGlobalScope.moduleName; const moduleName = path.relative( process.cwd(), path.resolve(path.dirname(currentModuleName), moduleSpecifier), ); return moduleName; } export function getGlobalScopeByModuleName( moduleName: string, globalScopes: Array, ) { const res = globalScopes.find((s) => s.moduleName === moduleName); if (!res) { throw Error(`no such module: ${moduleName}`); } return res; } export function getImportIdentifierName( importDeclaration: ts.ImportDeclaration | ts.ExportDeclaration, ) { const importIdentifierArray: string[] = []; const nameAliasImportMap = new Map(); let nameScopeImportName: string | null = null; let defaultImportName: string | null = null; let importClause = undefined; let namedBindings = undefined; if (ts.isImportDeclaration(importDeclaration)) { importClause = importDeclaration.importClause; if (!importClause) { /** import "otherModule" */ throw new UnimplementError( 'TODO: importing modules with side effects', ); } namedBindings = importClause.namedBindings; const importElement = importClause.name; if (importElement) { /** * import default export from other module * import module_case4_var1 from './module-case4'; */ const importElementName = importElement.getText(); defaultImportName = importElementName; } } else namedBindings = importDeclaration.exportClause; if (namedBindings) { if ( ts.isNamedImports(namedBindings) || ts.isNamedExports(namedBindings) ) { /** import regular exports from other module */ for (const importSpecifier of namedBindings.elements) { const specificIdentifier = importSpecifier.name; const specificName = specificIdentifier.getText()!; const propertyIdentifier = importSpecifier.propertyName; if (propertyIdentifier) { /** import {module_case2_var1 as a, module_case2_func1 as b} from './module-case2'; */ const propertyName = (( propertyIdentifier )).getText()!; nameAliasImportMap.set(specificName, propertyName); importIdentifierArray.push(propertyName); } else { /** import {module_case2_var1, module_case2_func1} from './module-case2'; */ importIdentifierArray.push(specificName); } } } else if ( ts.isNamespaceImport(namedBindings) || ts.isNamespaceExport(namedBindings) ) { /** * import entire module into a variable * import * as xx from './yy' */ const identifier = namedBindings.name; nameScopeImportName = identifier.getText()!; } else { throw Error('unexpected case'); } } return { importIdentifierArray, nameScopeImportName, nameAliasImportMap, defaultImportName, }; } export function getExportIdentifierName( exportDeclaration: ts.ExportDeclaration, curGlobalScope: GlobalScope, importModuleScope: GlobalScope, ) { const nameAliasExportMap = new Map(); const exportIdentifierList: Expression[] = []; // only need to record export alias const exportClause = exportDeclaration.exportClause; if (!exportClause) { throw Error('exportClause is undefined'); } if (ts.isNamedExports(exportClause)) { const exportSpecifiers = exportClause.elements; for (const exportSpecifier of exportSpecifiers) { const specificIdentifier = exportSpecifier.name; let specificName = specificIdentifier.getText()!; if (specificName === 'default') { specificName = ( importModuleScope!.defaultExpr as IdentifierExpression ).identifierName; curGlobalScope.addImportDefaultName( specificName, importModuleScope, ); } const specificExpr = new IdentifierExpression(specificName); const propertyIdentifier = exportSpecifier.propertyName; if (propertyIdentifier) { const propertyExpr = new IdentifierExpression( propertyIdentifier.getText(), ); const propertyName = (( propertyIdentifier )).getText()!; exportIdentifierList.push(propertyExpr); nameAliasExportMap.set(specificName, propertyName); } else { exportIdentifierList.push(specificExpr); } } } return { nameAliasExportMap, exportIdentifierList }; } export function getBuiltInFuncName(oriFuncName: string) { return BuiltinNames.builtinModuleName .concat(BuiltinNames.moduleDelimiter) .concat(oriFuncName); } export function getUtilsFuncName(name: string) { return BuiltinNames.utilsFuncName .concat(BuiltinNames.moduleDelimiter) .concat(name); } export interface SourceLocation { line: number; character: number; } export function getNodeLoc(node: ts.Node) { const sourceFile = node.getSourceFile(); const { line, character } = sourceFile.getLineAndCharacterOfPosition( node.getStart(sourceFile), ); // start from 1 return { line: line + 1, character: character }; } export function addSourceMapLoc(irNode: Statement | Expression, node: ts.Node) { const { line, character } = getNodeLoc(node); irNode.debugLoc = { line: line, character: character }; } // The character '\' in the string got from API getText is not treated // as a escape character. /** * @describe process escapes in a string * @param str the raw string got from API getText * @returns a new str */ export function processEscape(str: string) { const escapes1 = ['"', "'", '\\']; const escapes2 = ['n', 'r', 't', 'b', 'f']; const appendingStr = ['\n', '\r', '\t', '\b', '\f']; let newStr = ''; let code: string; for (let i = 0; i < str.length; i++) { if (str[i] == '\\' && i < str.length - 1) { if (escapes1.includes(str[i + 1])) { // binaryen will generate escape automaticlly for characters in escapes1 newStr += str[i + 1]; } else if (escapes2.includes(str[i + 1])) { newStr += appendingStr[escapes2.indexOf(str[i + 1])]; } else if (str[i + 1] == 'x') { code = decimalizationInternal(str.substring(i + 2, i + 4), 16); newStr += String.fromCharCode(parseFloat(code)); i += 2; } i += 1; continue; } if (escapes1.includes(str[i]) && (i == 0 || i == str.length - 1)) { continue; } newStr += str[i]; } return newStr; } function decimalizationInternal(value: string, systemNumeration: number) { let decimal = 0; let num = 0; let code = 0; for (let i = 0; i < value.length; i++) { code = value[i].charCodeAt(0); if (code >= 65 && code <= 70) num = 10 + code - 65; else if (code >= 97 && code <= 102) num = 10 + code - 97; else if (code >= 48 && code <= 59) num = parseFloat(value[i]); decimal = decimal * systemNumeration + num; } return decimal.toString(); } export function genericFunctionProcessor( genericFuncType: TSFunction, typeArguments: Type[], typeParameters: TSTypeParameter[], context: ParserContext, isMethod = false, doSpecialization = false, ): TSFunction { if ( typeArguments.length == 0 || typeParameters.length == 0 || (!isMethod && !genericFuncType.typeArguments) ) { return genericFuncType; } const genericOwner = genericFuncType.genericOwner ? genericFuncType.genericOwner : genericFuncType; let typeSignature = ''; if (doSpecialization && !isMethod) { const _typeParameters = genericFuncType.typeArguments!; typeSignature = getTypeSignature( _typeParameters, typeParameters, typeArguments, ); const found = TypeResolver.findTypeInSpecialiezedTypeCache( genericFuncType, typeSignature, ); if (found) return found as TSFunction; } const newFuncType = genericFuncType.clone(); newFuncType.setGenericOwner(genericOwner); newFuncType.setBelongedScope(undefined); if (doSpecialization) { if (!isMethod || (isMethod && genericFuncType.typeArguments)) { newFuncType.setSpecializedArguments(typeArguments); } // specialized function does not have typeArguments newFuncType.setTypeParameters(undefined); // iff method if (newFuncType.belongedClass) { newFuncType.belongedClass = undefined; } } else { // regenerate typeParameters if (!isMethod) { newFuncType.setSpecializedArguments(typeArguments); newFuncType.setTypeParameters([]); genericFuncType.typeArguments!.forEach((t) => { newFuncType.addTypeParameter( genericCoreProcessor( t, typeArguments, typeParameters, context, ) as TSTypeParameter, ); }); } } // regenerate the parameters newFuncType.setParamTypes([]); genericFuncType.getParamTypes().forEach((paramType) => { const newParamType = genericCoreProcessor( paramType, typeArguments, typeParameters, context, true, ); newFuncType.addParamType(newParamType); }); // prevent infinite recursive call if ( isMethod && (genericFuncType.funcKind == FunctionKind.CONSTRUCTOR || (genericFuncType.returnType instanceof TSClass && genericFuncType.returnType.toString === genericFuncType.belongedClass!.toString)) ) return newFuncType; // update specializedTypeCache if (doSpecialization && !isMethod) { TypeResolver.updateSpecializedTypeCache( genericOwner, typeSignature, newFuncType, ); } newFuncType.returnType = genericCoreProcessor( genericFuncType.returnType, typeArguments, typeParameters, context, true, ); return newFuncType; } export function genericClassProcessor( genericClassType: TSClass, typeArguments: Type[], typeParameters: TSTypeParameter[], context: ParserContext, doSpecialization = false, namePrefix?: string, ): TSClass { if ( typeArguments.length == 0 || typeParameters.length == 0 || (!doSpecialization && !genericClassType.typeArguments) ) return genericClassType; let typeSignature = ''; if (doSpecialization) { const _typeParameters = genericClassType.typeArguments; if (_typeParameters) { typeSignature = getTypeSignature( _typeParameters, typeParameters, typeArguments, ); } const found = TypeResolver.findTypeInSpecialiezedTypeCache( genericClassType, typeSignature, ); if (found) return found as TSClass; } if (genericClassType.typeArguments) { let newClassType: TSClass; if (genericClassType.kind === TypeKind.CLASS) { newClassType = new TSClass(); } else { newClassType = new TSInterface(); } newClassType.hasDeclareCtor = genericClassType.hasDeclareCtor; newClassType.isDeclare = genericClassType.isDeclare; newClassType.isLiteral = genericClassType.isLiteral; newClassType.overrideOrOwnMethods = genericClassType.overrideOrOwnMethods; newClassType.traverseStatus = genericClassType.traverseStatus; const newClassName = doSpecialization ? genericClassType.className + typeSignature : namePrefix ? namePrefix + '_' + genericClassType.className : genericClassType.className; newClassType.setClassName(newClassName); const genericOwner = genericClassType.genericOwner ? genericClassType.genericOwner : genericClassType; newClassType.setGenericOwner(genericOwner as TSClass); newClassType.setSpecializedArguments(typeArguments); if (doSpecialization) { if (genericClassType.mangledName !== '') { const genericMangledName = genericClassType.mangledName; newClassType.mangledName = genericMangledName.substring( 0, genericMangledName.lastIndexOf('|') + 1, ) + newClassName; } // update specializedTypeCache TypeResolver.updateSpecializedTypeCache( genericOwner, newClassName, newClassType, ); } else { // regenerate typeParameters newClassType.setTypeParameters([]); genericClassType.typeArguments!.forEach((t) => { newClassType.addTypeParameter( genericCoreProcessor( t, typeArguments, typeParameters, context, ) as TSTypeParameter, ); }); } // base class type if (genericClassType.getBase()) { let baseType = genericClassType.getBase(); if (baseType) { if (isTypeGeneric(baseType)) { baseType = genericClassProcessor( baseType, typeArguments, typeParameters, context, doSpecialization, namePrefix, ) as TSClass; } newClassType.setBase(baseType); } } const implInfc = genericClassType.getImplInfc(); if (implInfc && isTypeGeneric(implInfc)) { const newInfcType = genericCoreProcessor( implInfc, typeArguments, typeParameters, context, doSpecialization, namePrefix, ) as TSInterface; newClassType.setImplInfc(newInfcType); } else { newClassType.setImplInfc(implInfc); } genericClassType.fields.forEach((field) => { const newFieldType = genericCoreProcessor( field.type, typeArguments, typeParameters, context, doSpecialization, ); newClassType.addMemberField({ name: field.name, type: newFieldType, }); }); genericClassType.memberFuncs.forEach((func) => { const funcName = func.name; const funcKind = func.type.funcKind; let realFuncName = funcName; if (funcKind == FunctionKind.GETTER) { realFuncName = 'get_' + funcName; } else if (funcKind == FunctionKind.SETTER) { realFuncName = 'set_' + funcName; } const isOwnMethod = newClassType.overrideOrOwnMethods.has(realFuncName); if (isOwnMethod) { const newFuncType = genericFunctionProcessor( func.type, typeArguments, typeParameters, context, true, doSpecialization, ) as TSFunction; newFuncType.belongedClass = newClassType; newClassType.addMethod({ name: funcName, type: newFuncType, }); } else { let base = newClassType.getBase(); let found = func; while (base) { const isOwnMethod = base.overrideOrOwnMethods.has(realFuncName); if (isOwnMethod) { base.memberFuncs.forEach((f) => { if ( f.name == funcName && f.type.funcKind == funcKind ) { found = f; return; } }); break; } base = base.getBase(); } newClassType.addMethod(found); } }); genericClassType.staticFields.forEach((field) => { const newStaticFieldType = genericCoreProcessor( field.type, typeArguments, typeParameters, context, doSpecialization, ); if (newStaticFieldType instanceof TSTypeWithArguments) newClassType.addStaticMemberField({ name: field.name, type: newStaticFieldType, }); }); if (genericClassType.ctorType) { const newCtor = genericFunctionProcessor( genericClassType.ctorType, typeArguments, typeParameters, context, true, doSpecialization, ) as TSFunction; newCtor.belongedClass = newClassType; newClassType.ctorType = newCtor; newClassType.ctorType.returnType = newClassType; } if (genericClassType.belongedScope) if (doSpecialization) { genericClassType.belongedScope.parent?.addType( newClassType.className, newClassType, ); } else { const typeNames = new Array(); typeArguments.forEach((v) => { typeNames.push(`${(v as TSTypeParameter).name}`); }); const typeSignature = '<' + typeNames.join(',') + '>'; genericClassType.belongedScope.parent?.addType( newClassType.className + typeSignature, newClassType, ); } return newClassType; } else { if (doSpecialization) { /** * class Base { * x: number; * action(param: T) {....} * } */ genericClassType.memberFuncs.forEach((func) => { if (isTypeGeneric(func.type)) { const genericFuncType = func.type; const genericFunctionScope = genericFuncType.belongedScope!; // generate the name of the specialized function name const typeNames = new Array(); typeArguments.forEach((v) => { if (v.kind !== TypeKind.TYPE_PARAMETER) { if (v instanceof TSClass) { typeNames.push(v.className); } else { typeNames.push(`${v.kind}`); } } }); const typeSignature = typeNames.length > 0 ? '<' + typeNames.join(',') + '>' : ''; const genericFunctionScopeName = genericFunctionScope.getName(); const specializedFunctionScopeName = genericFunctionScopeName + typeSignature; // whether the specialized generic method already exists const existMethod = genericClassType.getMethod( specializedFunctionScopeName, ); if (existMethod.method) return genericClassType; methodSpecialize(genericFuncType, typeArguments, context); } }); } return genericClassType; } } /** * class A { * a: number; * echo(param: T) {...}; * } * const a = new A(); * this class type does not contain 'typeParameters'. */ export function methodSpecialize( genericMethodType: TSFunction, typeArguments: Type[], context: ParserContext, ) { const classType = genericMethodType.belongedClass; const originalFunctionScope = genericMethodType.belongedScope; const typeParameters = genericMethodType.typeArguments; // insufficient information used for specialization if (!classType || !typeParameters || !originalFunctionScope) return; const typeNames = new Array(); typeArguments.forEach((v) => { if (v instanceof TSClass) { typeNames.push(v.className); } else { typeNames.push(`${v.kind}`); } }); const typeSignature = '<' + typeNames.join(',') + '>'; const newFuncName = originalFunctionScope.getName() + typeSignature; const specializedFunctionType = genericFunctionProcessor( genericMethodType, typeArguments, typeParameters, context, true, true, ) as TSFunction; specializedFunctionType.belongedClass = classType; // create new function scope begin const newFuncScope = new FunctionScope(originalFunctionScope.parent!); originalFunctionScope.specialize(newFuncScope); newFuncScope.setClassName(classType.className); newFuncScope.setFuncName(newFuncName); newFuncScope.setFuncType(specializedFunctionType); specializedFunctionType.setBelongedScope(newFuncScope); if (originalFunctionScope.mangledName !== '') { const genericMangledName = originalFunctionScope.mangledName; const reverse = genericMangledName.split('|').reverse(); // class name reverse[0] = newFuncName; newFuncScope.mangledName = reverse.reverse().join('|'); } newFuncScope.setGenericOwner(originalFunctionScope); originalFunctionScope.addSpecializedScope(typeSignature, newFuncScope); const optional = classType.getMethod(originalFunctionScope.getName()) .method!.optional; classType.addMethod({ name: newFuncName, type: specializedFunctionType, optional: optional, }); classType.overrideOrOwnMethods.add(newFuncName); // specialize a generic member function on the inheritance chain const drivedClasses = classType.getDrivedClasses(); if (!drivedClasses) return; drivedClasses.forEach((c) => { if (c.memberFuncs.length > 0) { c.memberFuncs.forEach((m) => { methodSpecialize(m.type, typeArguments, context); }); } }); } /** * @describe specialize a generic type OR update the generic typeParameters * @param genericType the generic type that need to be updated or specialized * @param typeArguments type arguments * @param typeParameters generic parameter types * @param context the parser context * @param doSpecialization specialize the generic type when it is true; update the generic typeParameters when it is false * @param namePrefix in the generic inheritance chain, we will generate a new base class, and the new base class will be renamed * @returns a new generic type */ export function genericCoreProcessor( genericType: Type, typeArguments: Type[], typeParameters: TSTypeParameter[], context: ParserContext, doSpecialization = false, namePrefix?: string, ): Type { // the type being processed must be generic type if (!isTypeGeneric(genericType)) return genericType; switch (genericType.kind) { case TypeKind.VOID: case TypeKind.BOOLEAN: case TypeKind.NUMBER: case TypeKind.ANY: case TypeKind.UNDEFINED: case TypeKind.STRING: case TypeKind.UNKNOWN: case TypeKind.NULL: case TypeKind.WASM_I32: case TypeKind.WASM_I64: case TypeKind.WASM_F32: case TypeKind.WASM_F64: case TypeKind.WASM_ANYREF: { return genericType; } case TypeKind.ARRAY: { return new TSArray( genericCoreProcessor( (genericType as TSArray).elementType, typeArguments, typeParameters, context, doSpecialization, namePrefix, ), ); } case TypeKind.UNION: { const unionType = genericType as TSUnion; const newUnion = new TSUnion(); unionType.types.forEach((t) => { if (t.kind == TypeKind.UNDEFINED) { newUnion.addType(t); } else { const newType = genericCoreProcessor( t, typeArguments, typeParameters, context, doSpecialization, namePrefix, ); newUnion.addType(newType); } }); return newUnion; } case TypeKind.FUNCTION: { const newFuncType = genericFunctionProcessor( genericType as TSFunction, typeArguments, typeParameters, context, false, doSpecialization, ); return newFuncType; } case TypeKind.CLASS: case TypeKind.INTERFACE: { const newClassType = genericClassProcessor( genericType as TSClass, typeArguments, typeParameters, context, doSpecialization, namePrefix, ); return newClassType; } case TypeKind.TYPE_PARAMETER: { const gType = genericType as TSTypeParameter; if (typeArguments && typeParameters) { for (let i = 0; i < typeParameters.length; i++) { if (typeParameters[i].name === gType.name) { return typeArguments[i]; } } } return builtinTypes.get('any')!; } default: { throw new UnimplementError( `Not implemented type: ${genericType.kind}`, ); } } } /** * @describe specialize a generic type, or update the parameter type list of this generic type * @param genericType the generic type that need to be processed * @param typeArguments specialized type arguments list * @param typeParameters generic parameter type list * @param context the parser context * @param namePrefix in the generic inheritance chain, we will generate a new base class, and the new base class will be renamed. * e.g. * class A {...}; * class B extends A {...} * * In this case, we need to generate a new generic type B_A, so that its specialization operation will not affect its original type A. * At this time, the namePrefix is 'B_'. * @returns a new type */ export function processGenericType( genericType: Type, typeArguments: Type[], typeParameters: TSTypeParameter[], context: ParserContext, namePrefix?: string, ): Type { if ( !isTypeGeneric(genericType) || typeArguments.length == 0 || typeParameters.length == 0 ) return genericType; if (genericType instanceof TSUnion) { const newUnionType = new TSUnion(); genericType.types.forEach((t) => { newUnionType.addType( processGenericType(t, typeArguments, typeParameters, context), ); }); return newUnionType; } if (genericType instanceof TSArray) { const newArrayType = new TSArray( processGenericType( genericType.elementType, typeArguments, typeParameters, context, ), ); return newArrayType; } let newType: Type = genericType; // if updating the parameter type list const isUpdateTypeParameters = typeArguments.filter((type) => isTypeGeneric(type)).length == typeArguments.length; if (isUpdateTypeParameters) { // determine whether typeArguments is equal to typeParameters let isSame = true; for (let i = 0; i < typeParameters.length; i++) { if ( typeParameters[i].name !== (typeArguments[i] as TSTypeParameter).name ) { isSame = false; } } if (!isSame) newType = genericCoreProcessor( genericType, typeArguments, typeParameters, context, false, namePrefix, ); } else { if (genericType instanceof TSTypeWithArguments) { let typeSignature = ''; const _typeParameters = genericType.typeArguments; if (_typeParameters) { typeSignature = getTypeSignature( _typeParameters, typeParameters, typeArguments, ); } const found = TypeResolver.findTypeInSpecialiezedTypeCache( genericType, typeSignature, ); if (found) return found; } newType = genericCoreProcessor( genericType, typeArguments, typeParameters, context, true, ); if (genericType instanceof TSTypeWithArguments) { const genericOwner = genericType.genericOwner ? genericType.genericOwner : genericType; if (genericOwner.belongedScope) createScopeBySpecializedType( newType as TSTypeWithArguments, genericOwner.belongedScope.parent!, context, ); } } return newType; } export function createScopeBySpecializedType( specializeType: TSTypeWithArguments, parentScope: Scope, context: ParserContext, ) { const genericOwner = specializeType.genericOwner; if (!genericOwner) return; const genericScope = genericOwner.belongedScope; if (!genericScope) return; switch (genericScope.kind) { case ScopeKind.ClassScope: { createClassScope(specializeType as TSClass, parentScope, context); break; } case ScopeKind.FunctionScope: { createFunctionScope( specializeType as TSFunction, parentScope, false, context, ); break; } default: return; } } /** * @describe create a new specialized classScope * @param specializedClassType the specialized class type * @param parentScope the parent scope * @param context the parser context * @returns a new specialized ClassScope */ function createClassScope( specializedClassType: TSClass, parentScope: Scope, context: ParserContext, ) { const genericClassType = specializedClassType.genericOwner; if ( !genericClassType || (genericClassType && !genericClassType.belongedScope) || !specializedClassType.specializedArguments ) return; const genericClassScope = genericClassType.belongedScope! as ClassScope; const name = specializedClassType.className; // check if a specialized scope already exists if ( genericClassScope.specializedScopes && genericClassScope.specializedScopes.has(name) ) return; const newClassScope = new ClassScope(parentScope); genericClassScope.specialize(newClassScope); newClassScope.setName(name); newClassScope.setGenericOwner(genericClassScope); genericClassScope.addSpecializedScope(name, newClassScope); newClassScope.setClassType(specializedClassType); specializedClassType.setBelongedScope(newClassScope); if (genericClassScope.mangledName !== '') { const genericMangledName = genericClassScope.mangledName; const reverse = genericMangledName.split('|').reverse(); reverse[0] = name; newClassScope.mangledName = reverse.reverse().join('|'); specializedClassType.mangledName = newClassScope.mangledName; } genericClassScope.children.forEach((s) => { const genericFuncScope = s as FunctionScope; const funcKind = genericFuncScope.funcType.funcKind; // constructor is not in the memberFuncs if (funcKind == FunctionKind.CONSTRUCTOR) { createFunctionScope( specializedClassType.ctorType, newClassScope, true, context, ); } else { let funcName = genericFuncScope.getName(); // the function names of the getter and setter contain 'get_' and 'set_' prefix strings if ( funcKind == FunctionKind.GETTER || funcKind == FunctionKind.SETTER ) { funcName = funcName.substring(4); } const res = specializedClassType.memberFuncs.findIndex((f) => { return funcName === f.name && funcKind === f.type.funcKind; }); if (res !== -1) { const specializedFunctionType = specializedClassType.memberFuncs[res].type; createFunctionScope( specializedFunctionType, newClassScope, true, context, ); } } }); const genericBaseClassType = genericClassScope.classType.getBase(); const specializedBaseClassType = specializedClassType.getBase(); if (genericBaseClassType && specializedBaseClassType) { const genericBaseClassScope = genericBaseClassType.belongedScope; if (isTypeGeneric(genericBaseClassType) && genericBaseClassScope) { createScopeBySpecializedType( specializedBaseClassType, genericBaseClassScope.parent!, context, ); } } return newClassScope; } /** * @describe create a new specialize FunctionScope * @param specializedFunctionType the new specialized function type * @param parentScope the parent scope * @param isMethod method or not * @param context the parser context * @returns a new specialized FunctionScope */ function createFunctionScope( specializedFunctionType: TSFunction, parentScope: Scope, isMethod: boolean, context: ParserContext, ) { const typeArguments = isMethod ? (parentScope as ClassScope).classType.specializedArguments ? (parentScope as ClassScope).classType.specializedArguments : specializedFunctionType.specializedArguments : specializedFunctionType.specializedArguments; if ( !specializedFunctionType.genericOwner || (specializedFunctionType.genericOwner && !specializedFunctionType.genericOwner.belongedScope) || !typeArguments ) return; const genericFuncType = specializedFunctionType.genericOwner as TSFunction; const genericFunctionScope = genericFuncType.belongedScope! as FunctionScope; let typeSignature = ''; // check if a specialized scope already exists if (!isMethod) { const typeArgumentsSignature: Array = []; typeArguments.forEach((t) => { if (t.kind !== TypeKind.TYPE_PARAMETER) { if (t instanceof TSClass) { typeArgumentsSignature.push(t.className); } else { typeArgumentsSignature.push(`${t.kind}`); } } else { typeArgumentsSignature.push(`${(t as TSTypeParameter).name}`); } }); typeSignature = typeArgumentsSignature.length > 0 ? '<' + typeArgumentsSignature.join(',') + '>' : ''; if ( genericFunctionScope.specializedScopes && genericFunctionScope.specializedScopes.has( genericFunctionScope.getName() + typeSignature, ) ) return; } const newFuncScope = new FunctionScope(parentScope); newFuncScope.setGenericOwner(genericFunctionScope); genericFunctionScope.specialize(newFuncScope); if (isMethod) { newFuncScope.setClassName((parentScope as ClassScope).className); newFuncScope.setFuncName(genericFunctionScope.getName()); genericFunctionScope.addSpecializedScope( (parentScope as ClassScope).className, newFuncScope, ); } else { newFuncScope.setFuncName( genericFunctionScope.getName() + typeSignature, ); genericFunctionScope.addSpecializedScope( genericFunctionScope.getName() + typeSignature, newFuncScope, ); } newFuncScope.setFuncType(specializedFunctionType); specializedFunctionType.setBelongedScope(newFuncScope); const typeParameters = isMethod ? genericFuncType.belongedClass!.typeArguments : genericFuncType.typeArguments; if (!typeParameters) return; // process funcName, mangledName and className of FunctionScope if (genericFunctionScope.mangledName !== '') { const genericMangledName = genericFunctionScope.mangledName; const reverse = genericMangledName.split('|').reverse(); if (isMethod) { // class name reverse[1] = (parentScope as ClassScope).className; } else { reverse[0] = genericFunctionScope.getName() + typeSignature; } newFuncScope.mangledName = reverse.reverse().join('|'); } // Process function parameters and create scope for (let idx = 0; idx < genericFuncType.getParamTypes().length; idx++) { const genericParamType = genericFuncType.getParamTypes()[idx]; if ( genericParamType instanceof TSTypeWithArguments && genericParamType.belongedScope ) createScopeBySpecializedType( specializedFunctionType.getParamTypes()[ idx ] as TSTypeWithArguments, genericParamType.belongedScope.parent!, context, ); } return newFuncScope; } /* Check if the type, and all of its children contains generic type */ export function isTypeGeneric(type: Type): boolean { switch (type.kind) { case TypeKind.VOID: case TypeKind.BOOLEAN: case TypeKind.NUMBER: case TypeKind.ANY: case TypeKind.UNDEFINED: case TypeKind.STRING: case TypeKind.ENUM: case TypeKind.UNKNOWN: case TypeKind.NULL: case TypeKind.WASM_I32: case TypeKind.WASM_I64: case TypeKind.WASM_F32: case TypeKind.WASM_F64: case TypeKind.WASM_ANYREF: { return false; } case TypeKind.UNION: { const unionType = type as TSUnion; return unionType.types.some((t) => { return isTypeGeneric(t); }); } case TypeKind.TUPLE: { const tuple = type as TSTuple; const typeArr = tuple.elements; return typeArr.some((type) => { return isTypeGeneric(type); }); } case TypeKind.ARRAY: { return isTypeGeneric((type as TSArray).elementType); } case TypeKind.WASM_ARRAY: { const arr = (type as WasmArrayType).arrayType; return isTypeGeneric(arr.elementType); } case TypeKind.WASM_STRUCT: { const tuple = (type as WasmStructType).tupleType; return isTypeGeneric(tuple); } case TypeKind.FUNCTION: { const funcType = type as TSFunction; if ( (funcType.isMethod && funcType.belongedClass?.typeArguments) || funcType.typeArguments ) return true; return false; } case TypeKind.CLASS: case TypeKind.INTERFACE: { const classType = type as TSClass; if (classType.typeArguments) { /* When the class type carries type parameters, its method member does not contain type parameter information. * At this time, it is determined whether the function is a generic type by judging whether the class it belongs to is a generic type. * e.g. * class A { * a: T; * echo(param: T) {...}; * } */ return true; } else { /** * class A { * a: number; * echo(param: T) {...}; * } * const a = new A(); * this class type does not contain 'typeParameters', and newExpression does not contain 'typeArguments'. */ return classType.memberFuncs.some((func) => { return isTypeGeneric(func.type); }); } } case TypeKind.TYPE_PARAMETER: { return true; } default: { throw new UnimplementError(`Not implemented type: ${type.kind}`); } } } /** * @describe calculate 'typeArguments' from the formal parameters and actual parameters * @param formalParameters the formal parameters of this generic function * @param typeParameters the typeParameter list of this generic function * @param actualParameters the actual parameters of this generic function * @returns typeArguments */ // Currently only some basic types can be processed export function calculateTypeArguments( formalParameters: Type[], typeParameters: TSTypeParameter[], actualParameters: Type[], ) { if ( formalParameters.length == 0 || typeParameters.length == 0 || actualParameters.length == 0 ) return []; if (formalParameters.length > actualParameters.length) { formalParameters = formalParameters.slice(0, actualParameters.length); } const typeArguments = new Array(typeParameters.length); // argument type const argTypes = actualParameters; // TODO: Handling optional parameters for (let i = 0; i < formalParameters.length; i++) { const pType = formalParameters[i]; if (!isTypeGeneric(pType)) continue; const aType = argTypes[i]; if (pType instanceof TSTypeParameter) { const index = typeParameters.findIndex((t) => { return t.name === pType.name; }); if (index == -1) { throw new UnimplementError( `${pType.name} not found in typeParameters`, ); } typeArguments[index] = aType; } else if (pType instanceof TSTypeWithArguments) { const genericTypeList = pType.typeArguments!; const specializedTypeList = (aType as TSTypeWithArguments) .specializedArguments; if (specializedTypeList) { let idx = 0; genericTypeList.forEach((g) => { const index = typeParameters.findIndex((t) => { return t.name === g.name; }); if (index == -1) { throw new UnimplementError( `${g.name} not found in typeParameters`, ); } typeArguments[index] = specializedTypeList[idx]; idx++; }); } } else if (pType instanceof TSArray) { // workaround const genericElemType = pType.elementType; if (aType.kind !== TypeKind.ARRAY) { typeArguments[i] = aType; } else { if (genericElemType instanceof TSTypeParameter) { const specializedElemType = (aType as TSArray).elementType; const index = typeParameters.findIndex((t) => { return t.name === genericElemType.name; }); if (index == -1) { throw new UnimplementError( `${genericElemType.name} not found in typeParameters`, ); } typeArguments[index] = specializedElemType; } else { throw new UnimplementError( `generic Array specialization operation for complex elementType is not implemented`, ); } } } else if (pType instanceof TSUnion) { const types = pType.types; let i = 0; types.forEach((t) => { if (t.kind == TypeKind.TYPE_PARAMETER) return; i++; }); const index = typeParameters.findIndex((t) => { return t.name === (types[i] as TSTypeParameter).name; }); if (index == -1) { throw new UnimplementError( `${ (types[i] as TSTypeParameter).name } not found in typeParameters`, ); } typeArguments[index] = aType; } } return typeArguments; } /** * @describe generate signature from 'typeParameters' and 'typeArguments' * @param parameters the actual type parameters, for example: [Y] * @param typeParameters the type parameters collection, for example: [X, Y, Z] * @param typeArguments the type arguments collection, for example: [number, string, boolean] * @returns type signature, for example: '' */ export function getTypeSignature( parameters: TSTypeParameter[], typeParameters: TSTypeParameter[], typeArguments: Type[], ) { const typeNames: Array = []; parameters.forEach((type) => { const index = typeParameters.findIndex((t) => { return t.name === type.name; }); if (index == -1) { throw new UnimplementError( `${type.name} not found in typeParameters`, ); } if (typeArguments[index] instanceof TSClass) { typeNames.push((typeArguments[index] as TSClass).className); } else { typeNames.push(`${typeArguments[index].kind}`); } }); const typeSignature = typeNames.length > 0 ? '<' + typeNames.join(',') + '>' : ''; return typeSignature; } export enum PredefinedTypeId { VOID = 1, UNDEFINED, NULL, NEVER, INT, NUMBER, BOOLEAN, RAW_STRING, STRING, ANY, UNION, GENERIC, NAMESPACE, CLOSURECONTEXT, EMPTY, ARRAY, ARRAY_CONSTRUCTOR, STRING_OBJECT, STRING_CONSTRUCTOR, MAP, MAP_CONSTRUCTOR, SET, SET_CONSTRUCTOR, FUNCTION, PROMISE, PROMISE_CONSTRUCTOR, DATE, DATE_CONSTRUCTOR, FUNC_VOID_VOID_NONE, FUNC_VOID_VOID_DEFAULT, FUNC_VOID_ARRAY_ANY_DEFAULT, FUNC_ANY_ARRAY_ANY_DEFAULT, FUNC_VOID_VOID_METHOD, FUNC_VOID_ARRAY_ANY_METHOD, FUNC_ANY_ARRAY_ANY_METHOD, ARRAY_ANY, ARRAY_INT, ARRAY_NUMBER, ARRAY_BOOLEAN, ARRAY_STRING, SET_ANY, SET_INT, SET_NUMBER, SET_BOOLEAN, SET_STRING, MAP_STRING_STRING, MAP_STRING_ANY, MAP_INT_STRING, MAP_INT_ANY, ERROR, ERROR_CONSTRUCTOR, ARRAYBUFFER, ARRAYBUFFER_CONSTRUCTOR, DATAVIEW, DATAVIEW_CONSTRUCTOR, WASM_I64, WASM_F32, WASM_ARRAY, WASM_STRUCT, TUPLE, BUILTIN_TYPE_BEGIN, CUSTOM_TYPE_BEGIN = BUILTIN_TYPE_BEGIN + 1000, } export const DefaultTypeId = -1; export const CustomTypeId = PredefinedTypeId.CUSTOM_TYPE_BEGIN; export function getBuiltinType(typeStr: string): Type | undefined { if (builtinTypes.has(typeStr)) { return builtinTypes.get(typeStr); } else if (builtinWasmTypes.has(typeStr)) { return builtinWasmTypes.get(typeStr); } else { return undefined; } } export function isImportComment(obj: any): obj is Import { return obj && 'moduleName' in obj; } export function isNativeSignatureComment(obj: any): obj is NativeSignature { return obj && 'paramTypes' in obj; } export function isExportComment(obj: any): obj is Export { return obj && 'exportName' in obj; } export function isWASMArrayComment(obj: any): obj is WASMArray { return obj && 'WASMArray' in obj; } export function isWASMStructComment(obj: any): obj is WASMStruct { return obj && 'WASMStruct' in obj; } export function isPackedTypeKind(packedType: string) { return Object.values(PackedTypeKind).includes(packedType as PackedTypeKind); } export function isMutabilityKind(mutability: string) { return Object.values(MutabilityKind).includes(mutability as MutabilityKind); } export function isNullabilityKind(nullability: string) { return Object.values(NullabilityKind).includes( nullability as NullabilityKind, ); } export function parseComment(commentStr: string) { commentStr = commentStr.replace(/\s/g, ''); if (!commentStr.includes('Wasmnizer-ts')) { return null; } const commentKindReg = commentStr.match(/@([^@]+)@/); if (!commentKindReg) { return null; } const commentKind = commentKindReg[1]; switch (commentKind) { case CommentKind.NativeSignature: { const signatureStrReg = commentStr.match(/@([^@]+)$/); if (!signatureStrReg) { Logger.error('invalid signature in NativeSignature comment'); return null; } const signatureStr = signatureStrReg[1]; const signatureReg = signatureStr.match(/\(([^)]*)\)\s*=>\s*(\w+)/); if (!signatureReg) { Logger.error('invalid signature in NativeSignature comment'); return null; } const parameterTypesArr = signatureReg[1].split(/\s*,\s*/); const returnTypeStr = signatureReg[2]; const paramTypes: Type[] = []; for (const paramStr of parameterTypesArr) { const builtinType = getBuiltinType(paramStr); if (!builtinType) { Logger.error( 'unsupported signature type in NativeSignature comment', ); return null; } paramTypes.push(builtinType); } const builtinType = getBuiltinType(returnTypeStr); if (!builtinType) { Logger.error( 'unsupported signature type in NativeSignature comment', ); return null; } const returnType = builtinType; const obj: NativeSignature = { paramTypes: paramTypes, returnType: returnType, }; return obj; } case CommentKind.Import: { const importInfoReg = commentStr.match( /@Import@([a-zA-Z0-9_$]+),([a-zA-Z0-9_$]+$)/, ); if (!importInfoReg) { Logger.error('invalid information in Import comment'); return null; } const moduleName = importInfoReg[1]; const funcName = importInfoReg[2]; const obj: Import = { moduleName: moduleName, funcName: funcName, }; return obj; } case CommentKind.Export: { const exportInfoReg = commentStr.match(/@Export@([a-zA-Z0-9_$]+$)/); if (!exportInfoReg) { Logger.error('invalid information in Export comment'); return null; } const exportName = exportInfoReg[1]; const obj: Export = { exportName: exportName, }; return obj; } case CommentKind.WASMArray: { const basicArrayInfoReg = commentStr.match(/@WASMArray@\s*/); if (basicArrayInfoReg === null) { Logger.error('invalid information in WASMArray comment'); return null; } let packedTypeKind = PackedTypeKind.Not_Packed; let mutabilityKind = MutabilityKind.Mutable; let nullabilityKind = NullabilityKind.Nullable; const arrayInfoReg = commentStr.match( /@WASMArray@<\s*([^,]+),\s*([^,]+),\s*([^>]+)>/, ); if (arrayInfoReg && arrayInfoReg.length === 4) { Logger.info('use total message of WASMArray comment'); if ( !( isPackedTypeKind(arrayInfoReg[1]) && isMutabilityKind(arrayInfoReg[2]) && isNullabilityKind(arrayInfoReg[3]) ) ) { Logger.error('typo error in WASMArray comment'); return null; } packedTypeKind = arrayInfoReg[1] as PackedTypeKind; mutabilityKind = arrayInfoReg[2] as MutabilityKind; nullabilityKind = arrayInfoReg[3] as NullabilityKind; } const obj: WASMArray = { WASMArray: true, packedType: packedTypeKind, mutability: mutabilityKind, nullability: nullabilityKind, }; return obj; } case CommentKind.WASMStruct: { const basicStructInfoReg = commentStr.match(/@WASMStruct@\s*/); if (basicStructInfoReg === null) { Logger.error('invalid information in WASMStruct comment'); return null; } let obj: WASMStruct = { WASMStruct: true }; const structInfoReg = commentStr.match( /@WASMStruct@<\s*\[([^>]+)\],\s*\[([^>]+)\],\s*([^,]+),\s*([^>]+)>/, ); if (structInfoReg && structInfoReg.length === 5) { Logger.info('use total message of WASMStruct comment'); const nullabilityKind = structInfoReg[3]; if ( !( structInfoReg[1] .split(',') .every((item) => isPackedTypeKind(item)) && structInfoReg[2] .split(',') .every((item) => isMutabilityKind(item)) && isNullabilityKind(nullabilityKind) ) ) { Logger.error('typo error in WASMStruct comment'); return null; } const packedTypeKindArray = structInfoReg[1] .split(',') .map((item) => item.trim()); const mutabilityKindArray = structInfoReg[2] .split(',') .map((item) => item.trim()); obj = { WASMStruct: true, packedTypes: packedTypeKindArray as PackedTypeKind[], mutabilitys: mutabilityKindArray as MutabilityKind[], nullability: nullabilityKind as NullabilityKind, baseTypeName: structInfoReg[4], }; } return obj; } default: { Logger.error(`unsupported comment kind ${commentKind}`); return null; } } } export function parseCommentBasedNode(node: ts.Node) { const commentRanges = ts.getLeadingCommentRanges( node.getSourceFile().getFullText(), node.getFullStart(), ); let commentStrings: string[] = []; if (commentRanges?.length) { commentStrings = commentRanges.map((r) => node.getSourceFile().getFullText().slice(r.pos, r.end), ); } return commentStrings; } export function parseCommentBasedTypeAliasNode(node: ts.TypeAliasDeclaration) { const commentStrings = parseCommentBasedNode(node); if (commentStrings.length > 0) { /* only the last comment is the valid comment */ const validComment = commentStrings[commentStrings.length - 1]; const parseRes = parseComment(validComment); if (parseRes) { return parseRes; } } return null; } export function parseCommentBasedFuncNode( node: ts.FunctionLikeDeclaration, functionScope: FunctionScope, ) { const commentStrings = parseCommentBasedNode(node); if (commentStrings.length > 0) { for (const commentStr of commentStrings) { const parseRes = parseComment(commentStr); if ( parseRes && (isExportComment(parseRes) || isImportComment(parseRes) || isNativeSignatureComment(parseRes)) ) { const idx = functionScope.comments.findIndex((item) => { return ( (isExportComment(item) && isExportComment(parseRes)) || (isImportComment(item) && isImportComment(parseRes)) || (isNativeSignatureComment(item) && isNativeSignatureComment(parseRes)) ); }); if (idx !== -1) { functionScope.comments[idx] = parseRes; } else { functionScope.comments.push(parseRes); } } } } } export function convertWindowsPath(path: string) { if (process?.platform === 'win32') { // handle the edge-case of Window's long file names // See: https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#short-vs-long-names path = path.replace(/^\\\\\?\\/, ''); // convert the separators, valid since both \ and / can't be in a windows filename path = path.replace(/\\/g, '/'); // compress any // or /// to be just /, which is a safe oper under POSIX // and prevents accidental errors caused by manually doing path1+path2 path = path.replace(/\/\/+/g, '/'); } return path; } ================================================ FILE: src/variable.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import ts from 'typescript'; import { Expression, IdentifierExpression } from './expression.js'; import { TypeResolver, Type, TypeKind, TSEnum } from './type.js'; import { ParserContext } from './frontend.js'; import { addSourceMapLoc, generateNodeExpression, isScopeNode, } from './utils.js'; import { FunctionScope, GlobalScope, Scope } from './scope.js'; import { getConfig } from '../config/config_mgr.js'; export enum ModifierKind { default = '', const = 'const', let = 'let', var = 'var', } export class Variable { private _isClosure = false; public mangledName = ''; public scope: Scope | null = null; /* If variable is a closure variable, we should record which context it belongs to and its closureIdx */ public belongCtx?: Variable; public closureIndex?: number; /* If variable is a closure context, we should record its init context to do initialize */ public initContext?: Variable; public tsNode?: ts.Node; public needReBinding = false; constructor( private name: string, private type: Type, private modifiers: (ModifierKind | ts.SyntaxKind)[] = [], private index = -1, private isLocal = true, private init: Expression | null = null, ) {} get varName(): string { return this.name; } set varType(type: Type) { this.type = type; } get varType(): Type { return this.type; } get varModifiers(): (ModifierKind | ts.SyntaxKind)[] { return this.modifiers; } public isConst(): boolean { return this.modifiers.includes(ModifierKind.const); } public isReadOnly(): boolean { return this.modifiers.includes(ts.SyntaxKind.ReadonlyKeyword); } public isDeclare(): boolean { let res = false; if (this.modifiers.includes(ts.SyntaxKind.DeclareKeyword)) { res = true; return res; } return this.scope?.isDeclare() || false; } public isExport(): boolean { return this.modifiers.includes(ts.SyntaxKind.ExportKeyword); } public isDefault(): boolean { return this.modifiers.includes(ts.SyntaxKind.DefaultKeyword); } public isFuncScopedVar(): boolean { return this.modifiers.includes(ModifierKind.var); } public setInitExpr(expr: Expression) { this.init = expr; } get initExpression(): Expression | null { return this.init; } get varIsClosure(): boolean { return this._isClosure; } public setVarIndex(varIndex: number) { this.index = varIndex; } get varIndex(): number { return this.index; } public isLocalVar(): boolean { return this.isLocal; } public setIsLocalVar(isLocal: boolean): void { this.isLocal = isLocal; } public setVarIsClosure(): void { this._isClosure = true; } } export class Parameter extends Variable { private _isOptional: boolean; private _isDestructuring: boolean; constructor( name: string, type: Type, modifiers: (ModifierKind | ts.SyntaxKind)[] = [], index = -1, isOptional = false, isDestructuring = false, init: Expression | null = null, isLocal = true, ) { super(name, type, modifiers, index, isLocal, init); this._isOptional = isOptional; this._isDestructuring = isDestructuring; } get isOptional(): boolean { return this._isOptional; } get destructuring(): boolean { return this._isDestructuring; } } export class VariableScanner { typechecker: ts.TypeChecker | undefined = undefined; globalScopes = new Array(); currentScope: Scope | null = null; nodeScopeMap = new Map(); typeResolver: TypeResolver; constructor(private parserCtx: ParserContext) { this.globalScopes = this.parserCtx.globalScopes; this.nodeScopeMap = this.parserCtx.nodeScopeMap; this.typeResolver = this.parserCtx.typeResolver; } visit() { this.typechecker = this.parserCtx.typeChecker; this.nodeScopeMap.forEach((scope, node) => { this.currentScope = scope; ts.forEachChild(node, this.visitNode.bind(this)); }); } visitNode(node: ts.Node): void { switch (node.kind) { case ts.SyntaxKind.Parameter: { if ( node.parent.kind === ts.SyntaxKind.FunctionType || (node.parent && node.parent.parent.kind === ts.SyntaxKind.InterfaceDeclaration) ) { break; } if (ts.isIndexSignatureDeclaration(node.parent)) { break; } const parameterNode = node; const functionScope = ( this.currentScope!.getNearestFunctionScope() ); const paramName = parameterNode.name.getText(); let isDestructuring = false; if ( parameterNode.name.kind === ts.SyntaxKind.ObjectBindingPattern ) { isDestructuring = true; } const isOptional = parameterNode.questionToken || parameterNode.initializer ? true : false; const paramModifiers = []; if (parameterNode.modifiers !== undefined) { for (const modifier of parameterNode.modifiers) { paramModifiers.push(modifier.kind); } } const typeName = this.typeResolver.getTsTypeName(node); const paramType = functionScope.findType(typeName); const paramObj = new Parameter( paramName, paramType!, paramModifiers, -1, isOptional, isDestructuring, ); paramObj.tsNode = node; functionScope.addParameter(paramObj); break; } case ts.SyntaxKind.VariableDeclaration: { const variableDeclarationNode = node; const currentScope = this.currentScope!; let variableModifier = ModifierKind.default; if ( variableDeclarationNode.parent.kind === ts.SyntaxKind.VariableDeclarationList ) { const variableAssignText = variableDeclarationNode.parent.getText(); if (variableAssignText.includes(ModifierKind.const)) { variableModifier = ModifierKind.const; } else if (variableAssignText.includes(ModifierKind.let)) { variableModifier = ModifierKind.let; } else if (variableAssignText.includes(ModifierKind.var)) { variableModifier = ModifierKind.var; } } const varModifiers = []; varModifiers.push(variableModifier); const stmtNode = variableDeclarationNode.parent.parent; if (ts.isVariableStatement(stmtNode) && stmtNode.modifiers) { for (const modifier of stmtNode.modifiers) { varModifiers.push(modifier.kind); } } const variableName = variableDeclarationNode.name.getText(); const typeName = this.typeResolver.getTsTypeName(node); let variableType = currentScope.findType(typeName); if (!variableType) { throw new Error( `should get variableType for variable ${variableName}`, ); } if (variableType instanceof TSEnum) { variableType = variableType.memberType; } const variable = new Variable( variableName, variableType!, varModifiers, -1, true, ); variable.tsNode = node; if (variable.isDefault()) { currentScope.getRootGloablScope()!.defaultExpr = new IdentifierExpression(variable.varName); } /** Variables defined by var can be defined repeatedly */ const funcScope = currentScope.getNearestFunctionScope(); let belongScope: Scope; if (funcScope) { if (variable.isFuncScopedVar()) { belongScope = funcScope; } else { belongScope = currentScope; } } else { belongScope = currentScope; } const existVar = belongScope.findVariable(variableName, false); if (!existVar) { belongScope.addVariable(variable); } break; } default: { if (isScopeNode(node)) { break; } ts.forEachChild(node, this.visitNode.bind(this)); } } } } export class VariableInit { typechecker: ts.TypeChecker | undefined = undefined; globalScopes = new Array(); currentScope: Scope | null = null; nodeScopeMap = new Map(); constructor(private parserCtx: ParserContext) { this.globalScopes = this.parserCtx.globalScopes; this.nodeScopeMap = this.parserCtx.nodeScopeMap; } visit() { this.typechecker = this.parserCtx.typeChecker; this.nodeScopeMap.forEach((scope, node) => { this.currentScope = scope; ts.forEachChild(node, this.visitNode.bind(this)); }); } visitNode(node: ts.Node): void { switch (node.kind) { case ts.SyntaxKind.Parameter: { if ( node.parent.kind === ts.SyntaxKind.FunctionType || (node.parent && node.parent.parent.kind === ts.SyntaxKind.InterfaceDeclaration) ) { break; } if (ts.isIndexSignatureDeclaration(node.parent)) { break; } const parameterNode = node; const functionScope = ( this.currentScope!.getNearestFunctionScope() ); const paramName = parameterNode.name.getText(); const paramObj = functionScope.findVariable(paramName); if (!paramObj) { throw new Error( "don't find " + paramName + ' in current scope', ); } if (parameterNode.initializer) { const paramInit = generateNodeExpression( this.parserCtx.expressionProcessor, parameterNode.initializer, ); paramObj.setInitExpr(paramInit); } break; } case ts.SyntaxKind.VariableDeclaration: { const variableDeclarationNode = node; const currentScope = this.currentScope!; const variableName = variableDeclarationNode.name.getText(); const variableObj = currentScope.findVariable(variableName); if (!variableObj) { throw new Error( "don't find " + variableName + ' in current scope', ); } if ( !variableObj.isFuncScopedVar() && variableDeclarationNode.initializer ) { this.parserCtx.currentScope = currentScope; const variableInit = generateNodeExpression( this.parserCtx.expressionProcessor, variableDeclarationNode.initializer, ); variableObj.setInitExpr(variableInit); if (getConfig().sourceMap) { addSourceMapLoc( variableInit, variableDeclarationNode.initializer, ); } } else { /* TSC can ensure that variables(except those whose type is 'any') have been assigned before we use them. So when we directly use a variable that has not been assigned a value, the compiler will report an error: 'Variable is used before being assigned.' */ // When we use an 'any' type variable that has not been assigned an initial value, the compiler can default its initial value to 'undefined'. if ( (variableObj.varType.kind == TypeKind.ANY || variableObj.varType.kind == TypeKind.UNDEFINED) && !variableObj.isDeclare() && !variableDeclarationNode.initializer ) { variableObj.setInitExpr( new IdentifierExpression('undefined'), ); } } break; } default: { if (isScopeNode(node)) { break; } ts.forEachChild(node, this.visitNode.bind(this)); } } } } ================================================ FILE: tests/benchmark/README.md ================================================ ## Wasmnizer-ts benchmarks These benchmarks are based on some open source efforts to measure performance of the `ts2wasm compiler`, please refer to below table for license information of every benchmark. | benchmark | source link | license | | :-----: | :-----: | :-----: | | binarytrees | [Programming-Language-Benchmarks](https://github.com/hanabi1224/Programming-Language-Benchmarks/blob/main/bench/algorithm/binarytrees/1.ts) | [MIT License](./MIT_LICENSE.txt) | | mandelbrot | [wasm-mandelbrot](https://github.com/ColinEberhardt/wasm-mandelbrot/blob/master/assemblyscript/mandelbrot.ts) | | | merkletrees | [Programming-Language-Benchmarks](https://github.com/hanabi1224/Programming-Language-Benchmarks/blob/main/bench/algorithm/merkletrees/1.ts) | [MIT License](./MIT_LICENSE.txt) | | nbody | [Programming-Language-Benchmarks](https://github.com/hanabi1224/Programming-Language-Benchmarks/blob/main/bench/algorithm/nbody/6.ts) | [MIT License](./MIT_LICENSE.txt) | | spectral_norm | [The Benchmarks Game](https://benchmarksgame-team.pages.debian.net/benchmarksgame/program/spectralnorm-typescript-1.html) | [3-Clause BSD License](./BSD_LICENSE.txt) | ## Run 1. Prepare runtime environment - WAMR ``` bash cd runtime-library ./build.sh ``` - quickjs ``` bash cd runtime-library/deps/quickjs make export PATH=$(pwd):$PATH ``` 2. execute `run_benchmark.js` script ``` bash cd tests/benchmarks node run_benchmark.js # run multiple times to get average result node run_benchmark.js --times 3 # run specific benchmark node run_benchmark.js --benchmark binarytrees # run specific runtime mode node run_benchmark.js --runtimes wamr-aot # (wamr-aot | wamr-interp | qjs | node) # get result after multiple times warm up node run_benchmark.js --warmup 3 ``` ## Validate benchmark result When writing benchmarks, it is recommended to add verification of the benchmark execution results. One approach is to print `Validate result error when executing [benchmark name]` if the execution result is incorrect. For example, to validate the result of `quicksort`: ```typescript if (arr[0] !== minimum || arr[size - 1] !== maxinum) { console.log('Validate result error when executing quicksort'); } ``` > Note: Currently Wasmnizer-ts is under functionality development, the performance optimization is not on high priority. ================================================ FILE: tests/benchmark/any_basic_type_access.js ================================================ "use strict"; /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ function main() { var size = 1e6; var val = 0; var res = 0; var expect = 499998500001; for (var i = 0; i < size; i++) { res += val; val = i; } if (res !== expect) { console.log('Validate result error in any type access (basic type)'); } return res; } main() ================================================ FILE: tests/benchmark/any_basic_type_access.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function main() { const size = 1e6; let val: any = 0; let res = 0; const expect = 499998500001; for (let i = 0; i < size; i++) { res += val; val = i; } if (res !== expect) { console.log('Validate result error in any type access (basic type)'); } return res; } ================================================ FILE: tests/benchmark/any_complex_type_access.js ================================================ "use strict"; /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ var Foo = /** @class */ (function () { function Foo() { this.x = 0; this.y = false; } return Foo; }()); function main() { var val = new Foo(); val.z = 0; var size = 4e5; var res = 0; var expect = 159998800002; for (var i = 0; i < size; i++) { res += val.x; val.x = i; res += val.z; val.z = i; } if (res !== expect) { console.log('Validate result error in any type access (complex type)'); } return res; } main() ================================================ FILE: tests/benchmark/any_complex_type_access.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class Foo { x = 0; y = false; } export function main() { const val: any = new Foo(); val.z = 0; const size = 4e5; let res = 0; const expect = 159998800002; for (let i = 0; i < size; i++) { res += val.x; val.x = i; res += val.z; val.z = i; } if (res !== expect) { console.log('Validate result error in any type access (complex type)'); } return res; } ================================================ FILE: tests/benchmark/array_access.js ================================================ "use strict"; /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ function main() { var size = 1e4; var arr = new Array(size); var expect = 49999995000000; var res = 0; for (var i = 0, j = 0; i < 1e7; i++, j++) { arr[j] = i; res += arr[j]; if (j >= size - 1) j = 0; } if (res !== expect) { console.log('Validate result error in array access'); } return res; } main() ================================================ FILE: tests/benchmark/array_access.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function main() { const size = 1e4; const arr = new Array(size); const expect = 49999995000000; let res = 0; for (let i = 0, j = 0; i < 1e7; i++, j++) { arr[j] = i; res += arr[j]; if (j >= size - 1) j = 0; } if (res !== expect) { console.log('Validate result error in array access'); } return res; } ================================================ FILE: tests/benchmark/array_access_i32.js ================================================ "use strict"; /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ function main() { var size = 1e4; var arr = new Array(1e4); var expect = 49999995000000; var res = 0; for (var i = 0, j = 0; i < 1e7; i++, j++) { arr[j] = i; res += arr[j]; if (j >= size - 1) j = 0; } if (res !== expect) { console.log('Validate result error in array access (i32 index)'); } return res; } main() ================================================ FILE: tests/benchmark/array_access_i32.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ type i32 = number; export function main() { const size: i32 = 1e4; const arr = new Array(1e4); const expect = 49999995000000; let res = 0; for (let i = 0, j: i32 = 0; i < 1e7; i++, j++) { arr[j] = i; res += arr[j]; if (j >= size - 1) j = 0; } if (res !== expect) { console.log('Validate result error in array access (i32 index)'); } return res; } ================================================ FILE: tests/benchmark/binarytrees_class.js ================================================ "use strict"; /* This file is modified base on: * https://github.com/hanabi1224/Programming-Language-Benchmarks/blob/main/bench/algorithm/binarytrees/1.ts */ var Tree = /** @class */ (function () { function Tree(left, right) { this.left = null; this.right = null; this.left = left; this.right = right; } return Tree; }()); function main() { var maxDepth = Math.max(10, 0); var stretchDepth = maxDepth + 1; var stretchTree = createTree(stretchDepth); //console.log(`stretch tree of depth ${stretchDepth} check: ${checksum(stretchTree)}`) var longLivedTree = createTree(maxDepth); for (var depth = 4; depth <= maxDepth; depth += 2) { var iterations = Math.pow(2, maxDepth) - depth + 4; var sum = 0; for (var i = 0; i < iterations; i++) { var tree = createTree(depth); sum += checksum(tree); } // console.log(`${iterations} trees of depth ${depth} check: ${sum}`) } } function checksum(node) { if (!node) { return 1; } if (!node.left) { return 1; } return 1 + checksum(node.left) + checksum(node.right); } function createTree(depth) { if (depth > 0) { depth--; return new Tree(createTree(depth), createTree(depth)); } else { return new Tree(null, null); } } main() ================================================ FILE: tests/benchmark/binarytrees_class.ts ================================================ /* This file is modified base on: * https://github.com/hanabi1224/Programming-Language-Benchmarks/blob/main/bench/algorithm/binarytrees/1.ts */ interface ITreeNode { left: ITreeNode | null; right: ITreeNode | null; } class Tree implements ITreeNode { left: ITreeNode | null = null; right: ITreeNode | null = null; constructor(left: ITreeNode | null, right: ITreeNode | null) { this.left = left; this.right = right; } } export function main() { const maxDepth: number = Math.max(10, 0); const stretchDepth: number = maxDepth + 1; const stretchTree = createTree(stretchDepth); //console.log(`stretch tree of depth ${stretchDepth} check: ${checksum(stretchTree)}`) const longLivedTree = createTree(maxDepth); for (let depth = 4; depth <= maxDepth; depth += 2) { const iterations: number = Math.pow(2, maxDepth) - depth + 4; let sum = 0; for (let i = 0; i < iterations; i++) { const tree = createTree(depth); sum += checksum(tree); } // console.log(`${iterations} trees of depth ${depth} check: ${sum}`) } } function checksum(node: ITreeNode | null): number { if (!node) { return 1; } if (!node.left) { return 1; } return 1 + checksum(node.left) + checksum(node.right); } function createTree(depth: number): ITreeNode { if (depth > 0) { depth--; return new Tree(createTree(depth), createTree(depth)); } else { return new Tree(null, null); } } ================================================ FILE: tests/benchmark/binarytrees_interface.js ================================================ "use strict"; /* This file is modified base on: * https://github.com/hanabi1224/Programming-Language-Benchmarks/blob/main/bench/algorithm/binarytrees/1.ts */ function main() { var maxDepth = Math.max(10, 0); var stretchDepth = maxDepth + 1; var stretchTree = createTree(stretchDepth); //console.log(`stretch tree of depth ${stretchDepth} check: ${checksum(stretchTree)}`) var longLivedTree = createTree(maxDepth); for (var depth = 4; depth <= maxDepth; depth += 2) { var iterations = Math.pow(2, maxDepth) - depth + 4; var sum = 0; for (var i = 0; i < iterations; i++) { var tree = createTree(depth); sum += checksum(tree); } //console.log(`${iterations} trees of depth ${depth} check: ${sum}`) } } function checksum(node) { if (!node) { return 1; } if (!node.left) { return 1; } return 1 + checksum(node.left) + checksum(node.right); } function createTree(depth) { if (depth > 0) { depth--; return { left: createTree(depth), right: createTree(depth) }; } else { return { left: null, right: null }; } } main() ================================================ FILE: tests/benchmark/binarytrees_interface.ts ================================================ /* This file is modified base on: * https://github.com/hanabi1224/Programming-Language-Benchmarks/blob/main/bench/algorithm/binarytrees/1.ts */ interface ITreeNode { left: ITreeNode | null; right: ITreeNode | null; } export function main() { const maxDepth: number = Math.max(10, 0); const stretchDepth: number = maxDepth + 1; const stretchTree = createTree(stretchDepth); //console.log(`stretch tree of depth ${stretchDepth} check: ${checksum(stretchTree)}`) const longLivedTree = createTree(maxDepth); for (let depth = 4; depth <= maxDepth; depth += 2) { const iterations: number = Math.pow(2, maxDepth) - depth + 4; let sum = 0; for (let i = 0; i < iterations; i++) { const tree = createTree(depth); sum += checksum(tree); } //console.log(`${iterations} trees of depth ${depth} check: ${sum}`) } } function checksum(node: ITreeNode | null): number { if (!node) { return 1; } if (!node.left) { return 1; } return 1 + checksum(node.left) + checksum(node.right); } function createTree(depth: number): ITreeNode { if (depth > 0) { depth--; return { left: createTree(depth), right: createTree(depth) }; } else { return { left: null, right: null }; } } ================================================ FILE: tests/benchmark/class_access.js ================================================ "use strict"; /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ var Foo = /** @class */ (function () { function Foo(x) { this.x = x; } Foo.prototype.bar = function () { return this.x; }; return Foo; }()); function main() { var size = 1e7; var expect = 99999970000002; var res = 0; var f = new Foo(0); for (var i = 0; i < size; i++) { res += f.x; res += f.bar(); f.x = i; } if (res !== expect) { console.log('Validate result error in class access'); } return res; } main() ================================================ FILE: tests/benchmark/class_access.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class Foo { x: number; bar() { return this.x; } constructor(x: number) { this.x = x; } } export function main() { const size = 1e7; const expect = 99999970000002; let res = 0; const f = new Foo(0); for (let i = 0; i < size; i++) { res += f.x; res += f.bar(); f.x = i; } if (res !== expect) { console.log('Validate result error in class access'); } return res; } ================================================ FILE: tests/benchmark/class_allocation.js ================================================ "use strict"; /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ var Foo = /** @class */ (function () { function Foo(x) { this.x = x; } Foo.prototype.bar = function () { return this.x; }; return Foo; }()); function main() { var size = 1e7; var len = 10; var arr = new Array(len); for (var i = 0, j = 0; i < size; i++, j++) { if (j >= len) j = 0; arr[j] = new Foo(i); } return arr; } main() ================================================ FILE: tests/benchmark/class_allocation.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class Foo { x: number; bar() { return this.x; } constructor(x: number) { this.x = x; } } export function main() { const size = 1e7; const len = 10; const arr = new Array(len); for (let i = 0, j = 0; i < size; i++, j++) { if (j >= len) j = 0; arr[j] = new Foo(i); } return arr; } ================================================ FILE: tests/benchmark/compile_benchmark.js ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import fs from 'fs'; import path from 'path'; import { execSync } from 'child_process'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; const __filename = fileURLToPath(import.meta.url); const benchmark_dir = dirname(__filename); const benchmarks = fs.readdirSync(benchmark_dir); const compile_output_dir = path.join(benchmark_dir, 'compile_output'); const ts2wasm_script = path.join(benchmark_dir, '../../build/cli/ts2wasm.js'); const optimize_level = 3; let tsc_cmd; try { tsc_cmd = execSync('which tsc').toString().trim(); } catch (error) { if (process.env.TSC_PATH) { tsc_cmd = process.env.TSC_PATH; } else { const default_tsc_path = '/usr/local/bin/tsc'; if (fs.existsSync(default_tsc_path)) { tsc_cmd = default_tsc_path; } else { console.error("Error: TSC_PATH is not defined, and no default node path is provided."); process.exit(1); } } } console.log(`\x1b[33m======================== options ========================\x1b[0m`); console.log(`TSC_PATH: ${tsc_cmd}`); console.log(`\x1b[33m======================== compiling ========================\x1b[0m`); for (let benchmark of benchmarks) { let filename = path.basename(benchmark); let prefix = path.basename(filename, path.extname(filename)); let extension = path.extname(filename).slice(1); if (extension != 'ts') continue; fs.mkdirSync(compile_output_dir, { recursive: true }, (err) => { if (err) { console.error(`Failed to create ${compile_output_dir}:`, err); } }) console.log(`Compiling ${prefix} benchmark: `); execSync(`node ${ts2wasm_script} ${filename} --opt ${optimize_level} --output ${compile_output_dir}/${prefix}.wasm > tmp.txt`); console.log(` wasm target success`); execSync(`${tsc_cmd} ${filename} --outDir ${compile_output_dir} -m esnext`); console.log(` js target success`); } execSync(`rm -f tmp.txt`); ================================================ FILE: tests/benchmark/fibonacci.js ================================================ "use strict"; /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ function fibonacci(num) { if (num < 2) { return num; } else { return fibonacci(num - 1) + fibonacci(num - 2); } } function main() { fibonacci(35); } main() ================================================ FILE: tests/benchmark/fibonacci.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ function fibonacci(num: number): number { if (num < 2) { return num; } else { return fibonacci(num - 1) + fibonacci(num - 2); } } export function main() { fibonacci(35); } ================================================ FILE: tests/benchmark/interface_access_field_fastpath.js ================================================ "use strict"; /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ var Bar = /** @class */ (function () { function Bar() { this.x = 0; this.y = false; } return Bar; }()); function main() { var size = 1e6; var expect = 499998500001; var f = new Bar(); var res = 0; for (var i = 0; i < size; i++) { res += f.x; f.x = i; } if (res !== expect) { console.log('Validate result error in interface access field (fast path)'); } return res; } main() ================================================ FILE: tests/benchmark/interface_access_field_fastpath.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ interface Foo { x: number; y: boolean; z?: string; } class Bar { x = 0; y = false; z?: string; } export function main() { const size = 1e6; const expect = 499998500001; const f: Foo = new Bar(); let res = 0; for (let i = 0; i < size; i++) { res += f.x; f.x = i; } if (res !== expect) { console.log( 'Validate result error in interface access field (fast path)', ); } return res; } ================================================ FILE: tests/benchmark/interface_access_field_slowpath.js ================================================ "use strict"; /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ var Bar = /** @class */ (function () { function Bar() { this.y = false; this.x = 0; } return Bar; }()); function main() { var size = 1e6; var expect = 499998500001; var f = new Bar(); var res = 0; for (var i = 0; i < size; i++) { res += f.x; f.x = i; } if (res !== expect) { console.log('Validate result error in interface access field (slow path)'); } return res; } main() ================================================ FILE: tests/benchmark/interface_access_field_slowpath.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ interface Foo { x: number; y: boolean; z?: string; } class Bar { y = false; x = 0; } export function main() { const size = 1e6; const expect = 499998500001; const f: Foo = new Bar(); let res = 0; for (let i = 0; i < size; i++) { res += f.x; f.x = i; } if (res !== expect) { console.log( 'Validate result error in interface access field (slow path)', ); } return res; } ================================================ FILE: tests/benchmark/interface_access_method_fastpath.js ================================================ "use strict"; /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ var Bar = /** @class */ (function () { function Bar(x) { this.x = x; } Bar.prototype.bar = function (val) { this.x = val; return this.x; }; return Bar; }()); function main() { var size = 1e6; var expect = 499999500000; var f = new Bar(0); var res = 0; for (var i = 0; i < size; i++) { res += f.bar(i); } if (res !== expect) { console.log('Validate result error in interface access method (fast path)'); } return res; } main() ================================================ FILE: tests/benchmark/interface_access_method_fastpath.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ interface Foo { x: number; z?: boolean; bar(val: number): number; } class Bar { x: number; z?: boolean; bar(val: number) { this.x = val; return this.x; } constructor(x: number) { this.x = x; } } export function main() { const size = 1e6; const expect = 499999500000; const f: Foo = new Bar(0); let res = 0; for (let i = 0; i < size; i++) { res += f.bar(i); } if (res !== expect) { console.log( 'Validate result error in interface access method (fast path)', ); } return res; } ================================================ FILE: tests/benchmark/interface_access_method_slowpath.js ================================================ "use strict"; /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ var Bar = /** @class */ (function () { function Bar(x) { this.y = 'str'; this.z = false; this.x = x; } Bar.prototype.baz = function () { return this.y; }; Bar.prototype.bar = function (val) { this.x = val; return this.x; }; return Bar; }()); function main() { var size = 1e6; var expect = 499999500000; var f = new Bar(0); var res = 0; for (var i = 0; i < size; i++) { res += f.bar(i); } if (res !== expect) { console.log('Validate result error in interface access method (slow path)'); } return res; } main() ================================================ FILE: tests/benchmark/interface_access_method_slowpath.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ interface Foo { x: number; y: string; z?: boolean; bar(val: number): number; baz(): string; } class Bar { y = 'str'; x: number; z = false; baz() { return this.y; } bar(val: number) { this.x = val; return this.x; } constructor(x: number) { this.x = x; } } export function main() { const size = 1e6; const expect = 499999500000; const f: Foo = new Bar(0); let res = 0; for (let i = 0; i < size; i++) { res += f.bar(i); } if (res !== expect) { console.log( 'Validate result error in interface access method (slow path)', ); } return res; } ================================================ FILE: tests/benchmark/mandelbrot.js ================================================ "use strict"; /* This file is modified base on: * https://github.com/ColinEberhardt/wasm-mandelbrot/blob/master/assemblyscript/mandelbrot.ts */ // const WIDTH = 1200; // const HEIGHT = 800; function colour(iteration, offset, scale) { iteration = (iteration * scale + offset) & 1023; if (iteration < 256) { return iteration; } else if (iteration < 512) { return 255 - (iteration - 255); } return 0; } function iterateEquation(x0, y0, maxiterations) { var a = 0.0, b = 0.0, rx = 0.0, ry = 0.0, ab; var iterations = 0; while (iterations < maxiterations && rx * rx + ry * ry <= 4) { rx = a * a - b * b + x0; ab = a * b; ry = ab + ab + y0; a = rx; b = ry; iterations++; } return iterations; } function scale(domainStart, domainLength, screenLength, step) { return domainStart + domainLength * (step * (1.0 / screenLength) - 1); } function mandelbrot(data, HEIGHT, WIDTH, maxIterations, cx, cy, diameter) { var verticalDiameter = (diameter * HEIGHT) / WIDTH; for (var y = 0; y < HEIGHT; ++y) { for (var x = 0; x < WIDTH; ++x) { // convert from screen coordinates to mandelbrot coordinates var rx = scale(cx, diameter, WIDTH, x); var ry = scale(cy, verticalDiameter, HEIGHT, y); var iterations = iterateEquation(rx, ry, maxIterations); var outside = iterations == maxIterations; var idx = (x + y * WIDTH) << 2; data[idx + 0] = outside ? 0 : colour(iterations, 0, 4); data[idx + 1] = outside ? 0 : colour(iterations, 128, 4); data[idx + 2] = outside ? 0 : colour(iterations, 356, 4); data[idx + 3] = 255; } } } function main() { var WIDTH = 1200; var HEIGHT = 800; var data = new Array(WIDTH * HEIGHT * 4); mandelbrot(data, HEIGHT, WIDTH, 10000, -0.743644786, 0.1318252536, 0.00029336); } main() ================================================ FILE: tests/benchmark/mandelbrot.ts ================================================ /* This file is modified base on: * https://github.com/ColinEberhardt/wasm-mandelbrot/blob/master/assemblyscript/mandelbrot.ts */ // const WIDTH = 1200; // const HEIGHT = 800; function colour(iteration: number, offset: number, scale: number): number { iteration = (iteration * scale + offset) & 1023; if (iteration < 256) { return iteration; } else if (iteration < 512) { return 255 - (iteration - 255); } return 0; } function iterateEquation( x0: number, y0: number, maxiterations: number, ): number { let a = 0.0, b = 0.0, rx = 0.0, ry = 0.0, ab: number; let iterations = 0; while (iterations < maxiterations && rx * rx + ry * ry <= 4) { rx = a * a - b * b + x0; ab = a * b; ry = ab + ab + y0; a = rx; b = ry; iterations++; } return iterations; } function scale( domainStart: number, domainLength: number, screenLength: number, step: number, ): number { return domainStart + domainLength * (step * (1.0 / screenLength) - 1); } function mandelbrot( data: number[], HEIGHT: number, WIDTH: number, maxIterations: number, cx: number, cy: number, diameter: number, ) { const verticalDiameter = (diameter * HEIGHT) / WIDTH; for (let y = 0; y < HEIGHT; ++y) { for (let x = 0; x < WIDTH; ++x) { // convert from screen coordinates to mandelbrot coordinates const rx = scale(cx, diameter, WIDTH, x); const ry = scale(cy, verticalDiameter, HEIGHT, y); const iterations = iterateEquation(rx, ry, maxIterations); const outside = iterations == maxIterations; const idx = (x + y * WIDTH) << 2; data[idx + 0] = outside ? 0 : colour(iterations, 0, 4); data[idx + 1] = outside ? 0 : colour(iterations, 128, 4); data[idx + 2] = outside ? 0 : colour(iterations, 356, 4); data[idx + 3] = 255; } } } export function main() { const WIDTH = 1200; const HEIGHT = 800; const data: number[] = new Array(WIDTH * HEIGHT * 4); mandelbrot( data, HEIGHT, WIDTH, 10000, -0.743644786, 0.1318252536, 0.00029336, ); } ================================================ FILE: tests/benchmark/mandelbrot_i32.js ================================================ "use strict"; /* This file is modified base on: * https://github.com/ColinEberhardt/wasm-mandelbrot/blob/master/assemblyscript/mandelbrot.ts */ function colour(iteration, offset, scale) { iteration = (iteration * scale + offset) & 1023; if (iteration < 256) { return iteration; } else if (iteration < 512) { return 255 - (iteration - 255); } return 0; } function iterateEquation(x0, y0, maxiterations) { var a = 0.0, b = 0.0, rx = 0.0, ry = 0.0, ab; var iterations = 0; while (iterations < maxiterations && rx * rx + ry * ry <= 4) { rx = a * a - b * b + x0; ab = a * b; ry = ab + ab + y0; a = rx; b = ry; iterations++; } return iterations; } function scale(domainStart, domainLength, screenLength, step) { return domainStart + domainLength * (step * (1.0 / screenLength) - 1); } function mandelbrot(data, HEIGHT, WIDTH, maxIterations, cx, cy, diameter) { var verticalDiameter = (diameter * HEIGHT) / WIDTH; for (var y = 0; y < HEIGHT; ++y) { for (var x = 0; x < WIDTH; ++x) { // convert from screen coordinates to mandelbrot coordinates var rx = scale(cx, diameter, WIDTH, x); var ry = scale(cy, verticalDiameter, HEIGHT, y); var iterations = iterateEquation(rx, ry, maxIterations); var outside = iterations == maxIterations; var idx = (x + y * WIDTH) << 2; var maxIterationValue = 0; data[idx + 0] = outside ? maxIterationValue : colour(iterations, 0, 4); data[idx + 1] = outside ? maxIterationValue : colour(iterations, 128, 4); data[idx + 2] = outside ? maxIterationValue : colour(iterations, 356, 4); data[idx + 3] = 255; } } } function main() { var WIDTH = 1200; var HEIGHT = 800; var data = new Array(WIDTH * HEIGHT * 4); mandelbrot(data, HEIGHT, WIDTH, 10000, -0.743644786, 0.1318252536, 0.00029336); } main() ================================================ FILE: tests/benchmark/mandelbrot_i32.ts ================================================ /* This file is modified base on: * https://github.com/ColinEberhardt/wasm-mandelbrot/blob/master/assemblyscript/mandelbrot.ts */ // const WIDTH = 1200; // const HEIGHT = 800; type i32 = number; function colour(iteration: i32, offset: i32, scale: i32): i32 { iteration = (iteration * scale + offset) & 1023; if (iteration < 256) { return iteration; } else if (iteration < 512) { return 255 - (iteration - 255); } return 0; } function iterateEquation(x0: number, y0: number, maxiterations: i32): i32 { let a = 0.0, b = 0.0, rx = 0.0, ry = 0.0, ab: number; let iterations: i32 = 0; while (iterations < maxiterations && rx * rx + ry * ry <= 4) { rx = a * a - b * b + x0; ab = a * b; ry = ab + ab + y0; a = rx; b = ry; iterations++; } return iterations; } function scale( domainStart: number, domainLength: number, screenLength: number, step: number, ): number { return domainStart + domainLength * (step * (1.0 / screenLength) - 1); } function mandelbrot( data: i32[], HEIGHT: i32, WIDTH: i32, maxIterations: i32, cx: number, cy: number, diameter: number, ) { const verticalDiameter = (diameter * HEIGHT) / WIDTH; for (let y: i32 = 0; y < HEIGHT; ++y) { for (let x: i32 = 0; x < WIDTH; ++x) { // convert from screen coordinates to mandelbrot coordinates const rx = scale(cx, diameter, WIDTH, x); const ry = scale(cy, verticalDiameter, HEIGHT, y); const iterations: i32 = iterateEquation(rx, ry, maxIterations); const outside = iterations == maxIterations; const idx: i32 = (x + y * WIDTH) << 2; const maxIterationValue: i32 = 0; data[idx + 0] = outside ? maxIterationValue : colour(iterations, 0, 4); data[idx + 1] = outside ? maxIterationValue : colour(iterations, 128, 4); data[idx + 2] = outside ? maxIterationValue : colour(iterations, 356, 4); data[idx + 3] = 255; } } } export function main() { const WIDTH: i32 = 1200; const HEIGHT: i32 = 800; const data: i32[] = new Array(WIDTH * HEIGHT * 4); mandelbrot( data, HEIGHT, WIDTH, 10000, -0.743644786, 0.1318252536, 0.00029336, ); } ================================================ FILE: tests/benchmark/merkletrees.js ================================================ "use strict"; /* This file is modified base on: * https://github.com/hanabi1224/Programming-Language-Benchmarks/blob/main/bench/algorithm/merkletrees/1.ts */ var TreeNode = /** @class */ (function () { function TreeNode(value, left, right) { this.hash = null; this.value = value; this.left = left; this.right = right; } TreeNode.create = function (depth) { if (depth > 0) { var d = depth - 1; return new TreeNode(null, TreeNode.create(d), TreeNode.create(d)); } return new TreeNode(1, null, null); }; TreeNode.prototype.check = function () { if (this.hash != null) { if (this.value != null) { return true; } else if (this.left != null && this.right != null) { return this.left.check() && this.right.check(); } } return false; }; TreeNode.prototype.calHash = function () { if (this.hash == null) { if (this.value != null) { this.hash = this.value; } else if (this.left != null && this.right != null) { this.left.calHash(); this.right.calHash(); this.hash = this.left.getHash() + this.right.getHash(); } } }; TreeNode.prototype.getHash = function () { if (this.hash === null) { return -1; } else { return this.hash; } }; return TreeNode; }()); function main() { var maxDepth = Math.max(10, 0); var stretchDepth = maxDepth + 1; var stretchTree = TreeNode.create(stretchDepth); stretchTree.calHash(); // console.log(`stretch tree of depth ${stretchDepth}\t root hash: ${stretchTree.getHash()} check: ${stretchTree.check()}`); var longLivedTree = TreeNode.create(maxDepth); for (var depth = 4; depth <= maxDepth; depth += 2) { var iterations = 1 << (maxDepth - depth + 4); var sum = 0; for (var i = 0; i < iterations; i++) { var tree = TreeNode.create(depth); tree.calHash(); sum += tree.getHash(); } // console.log(`${iterations}\t trees of depth ${depth}\t root hash sum: ${sum}`); } longLivedTree.calHash(); // console.log( // `long lived tree of depth ${maxDepth}\t root hash: ${longLivedTree.getHash()} check: ${longLivedTree.check()}` // ); } main() ================================================ FILE: tests/benchmark/merkletrees.ts ================================================ /* This file is modified base on: * https://github.com/hanabi1224/Programming-Language-Benchmarks/blob/main/bench/algorithm/merkletrees/1.ts */ class TreeNode { hash: number | null = null; value: number | null; left: TreeNode | null; right: TreeNode | null; constructor( value: number | null, left: TreeNode | null, right: TreeNode | null, ) { this.value = value; this.left = left; this.right = right; } static create(depth: number): TreeNode { if (depth > 0) { const d = depth - 1; return new TreeNode(null, TreeNode.create(d), TreeNode.create(d)); } return new TreeNode(1, null, null); } check(): boolean { if (this.hash != null) { if (this.value != null) { return true; } else if (this.left != null && this.right != null) { return this.left.check() && this.right.check(); } } return false; } calHash(): void { if (this.hash == null) { if (this.value != null) { this.hash = this.value; } else if (this.left != null && this.right != null) { this.left.calHash(); this.right.calHash(); this.hash = this.left.getHash() + this.right.getHash(); } } } getHash(): number { if (this.hash === null) { return -1; } else { return this.hash!; } } } export function main() { const maxDepth: number = Math.max(10, 0); const stretchDepth = maxDepth + 1; const stretchTree = TreeNode.create(stretchDepth); stretchTree.calHash(); // console.log(`stretch tree of depth ${stretchDepth}\t root hash: ${stretchTree.getHash()} check: ${stretchTree.check()}`); const longLivedTree = TreeNode.create(maxDepth); for (let depth = 4; depth <= maxDepth; depth += 2) { const iterations = 1 << (maxDepth - depth + 4); let sum = 0; for (let i = 0; i < iterations; i++) { const tree = TreeNode.create(depth); tree.calHash(); sum += tree.getHash(); } // console.log(`${iterations}\t trees of depth ${depth}\t root hash sum: ${sum}`); } longLivedTree.calHash(); // console.log( // `long lived tree of depth ${maxDepth}\t root hash: ${longLivedTree.getHash()} check: ${longLivedTree.check()}` // ); } ================================================ FILE: tests/benchmark/nbody_class.js ================================================ "use strict"; /* The Computer Language Benchmarks Game https://salsa.debian.org/benchmarksgame-team/benchmarksgame/ contributed by Isaac Gouy modified by Andrey Filatkin modified for typescript by Isaac Gouy modified for deno runtime by hanabi1224 */ /* This file is modified base on: * https://github.com/hanabi1224/Programming-Language-Benchmarks/blob/main/bench/algorithm/nbody/6.ts */ // const PI = Math.PI; var PI = 3.141592653589793; var SOLAR_MASS = 4 * PI * PI; var DAYS_PER_YEAR = 365.24; var NBody = /** @class */ (function () { function NBody() { this.x = 0; this.y = 0; this.z = 0; this.vx = 0; this.vy = 0; this.vz = 0; this.mass = 0; } return NBody; }()); function Jupiter() { return { x: 4.8414314424647209, y: -1.16032004402742839, z: -1.03622044471123109e-1, vx: 1.66007664274403694e-3 * DAYS_PER_YEAR, vy: 7.69901118419740425e-3 * DAYS_PER_YEAR, vz: -6.90460016972063023e-5 * DAYS_PER_YEAR, mass: 9.54791938424326609e-4 * SOLAR_MASS }; } function Saturn() { return { x: 8.34336671824457987, y: 4.12479856412430479, z: -4.03523417114321381e-1, vx: -2.76742510726862411e-3 * DAYS_PER_YEAR, vy: 4.99852801234917238e-3 * DAYS_PER_YEAR, vz: 2.30417297573763929e-5 * DAYS_PER_YEAR, mass: 2.85885980666130812e-4 * SOLAR_MASS }; } function Uranus() { return { x: 1.2894369562139131e1, y: -1.51111514016986312e1, z: -2.23307578892655734e-1, vx: 2.96460137564761618e-3 * DAYS_PER_YEAR, vy: 2.3784717395948095e-3 * DAYS_PER_YEAR, vz: -2.96589568540237556e-5 * DAYS_PER_YEAR, mass: 4.36624404335156298e-5 * SOLAR_MASS }; } function Neptune() { return { x: 1.53796971148509165e1, y: -2.59193146099879641e1, z: 1.79258772950371181e-1, vx: 2.68067772490389322e-3 * DAYS_PER_YEAR, vy: 1.62824170038242295e-3 * DAYS_PER_YEAR, vz: -9.5159225451971587e-5 * DAYS_PER_YEAR, mass: 5.15138902046611451e-5 * SOLAR_MASS }; } function Sun() { return { x: 0.0, y: 0.0, z: 0.0, vx: 0.0, vy: 0.0, vz: 0.0, mass: SOLAR_MASS }; } var bodies = [Sun(), Jupiter(), Saturn(), Uranus(), Neptune()]; function offsetMomentum() { var px = 0; var py = 0; var pz = 0; var size = bodies.length; for (var i = 0; i < size; i++) { var body_1 = bodies[i]; var mass = body_1.mass; px += body_1.vx * mass; py += body_1.vy * mass; pz += body_1.vz * mass; } var body = bodies[0]; body.vx = -px / SOLAR_MASS; body.vy = -py / SOLAR_MASS; body.vz = -pz / SOLAR_MASS; } function advance(dt) { var size = bodies.length; for (var i = 0; i < size; i++) { var bodyi = bodies[i]; var vxi = bodyi.vx; var vyi = bodyi.vy; var vzi = bodyi.vz; for (var j = i + 1; j < size; j++) { var bodyj = bodies[j]; var dx = bodyi.x - bodyj.x; var dy = bodyi.y - bodyj.y; var dz = bodyi.z - bodyj.z; var d2 = dx * dx + dy * dy + dz * dz; var mag = dt / (d2 * Math.sqrt(d2)); var massj = bodyj.mass; vxi = vxi - dx * massj * mag; vyi = vyi - dy * massj * mag; vzi = vzi - dz * massj * mag; var massi = bodyi.mass; bodyj.vx = bodyj.vx + dx * massi * mag; bodyj.vy = bodyj.vy + dy * massi * mag; bodyj.vz = bodyj.vz + dz * massi * mag; } bodyi.vx = vxi; bodyi.vy = vyi; bodyi.vz = vzi; bodyi.x = bodyi.x + dt * vxi; bodyi.y = bodyi.y + dt * vyi; bodyi.z = bodyi.z + dt * vzi; } } function energy() { var e = 0; var size = bodies.length; for (var i = 0; i < size; i++) { var bodyi = bodies[i]; e += 0.5 * bodyi.mass * (bodyi.vx * bodyi.vx + bodyi.vy * bodyi.vy + bodyi.vz * bodyi.vz); for (var j = i + 1; j < size; j++) { var bodyj = bodies[j]; var dx = bodyi.x - bodyj.x; var dy = bodyi.y - bodyj.y; var dz = bodyi.z - bodyj.z; var distance = Math.sqrt(dx * dx + dy * dy + dz * dz); e -= (bodyi.mass * bodyj.mass) / distance; } } return e; } function main() { var n = 1000000; offsetMomentum(); energy(); for (var i = 0; i < n; i++) { advance(0.01); } energy(); } main() ================================================ FILE: tests/benchmark/nbody_class.ts ================================================ /* The Computer Language Benchmarks Game https://salsa.debian.org/benchmarksgame-team/benchmarksgame/ contributed by Isaac Gouy modified by Andrey Filatkin modified for typescript by Isaac Gouy modified for deno runtime by hanabi1224 */ /* This file is modified base on: * https://github.com/hanabi1224/Programming-Language-Benchmarks/blob/main/bench/algorithm/nbody/6.ts */ // const PI = Math.PI; const PI = 3.141592653589793; const SOLAR_MASS = 4 * PI * PI; const DAYS_PER_YEAR = 365.24; class NBody { x = 0; y = 0; z = 0; vx = 0; vy = 0; vz = 0; mass = 0; } function Jupiter(): NBody { return { x: 4.8414314424647209, y: -1.16032004402742839, z: -1.03622044471123109e-1, vx: 1.66007664274403694e-3 * DAYS_PER_YEAR, vy: 7.69901118419740425e-3 * DAYS_PER_YEAR, vz: -6.90460016972063023e-5 * DAYS_PER_YEAR, mass: 9.54791938424326609e-4 * SOLAR_MASS, }; } function Saturn(): NBody { return { x: 8.34336671824457987, y: 4.12479856412430479, z: -4.03523417114321381e-1, vx: -2.76742510726862411e-3 * DAYS_PER_YEAR, vy: 4.99852801234917238e-3 * DAYS_PER_YEAR, vz: 2.30417297573763929e-5 * DAYS_PER_YEAR, mass: 2.85885980666130812e-4 * SOLAR_MASS, }; } function Uranus(): NBody { return { x: 1.2894369562139131e1, y: -1.51111514016986312e1, z: -2.23307578892655734e-1, vx: 2.96460137564761618e-3 * DAYS_PER_YEAR, vy: 2.3784717395948095e-3 * DAYS_PER_YEAR, vz: -2.96589568540237556e-5 * DAYS_PER_YEAR, mass: 4.36624404335156298e-5 * SOLAR_MASS, }; } function Neptune(): NBody { return { x: 1.53796971148509165e1, y: -2.59193146099879641e1, z: 1.79258772950371181e-1, vx: 2.68067772490389322e-3 * DAYS_PER_YEAR, vy: 1.62824170038242295e-3 * DAYS_PER_YEAR, vz: -9.5159225451971587e-5 * DAYS_PER_YEAR, mass: 5.15138902046611451e-5 * SOLAR_MASS, }; } function Sun(): NBody { return { x: 0.0, y: 0.0, z: 0.0, vx: 0.0, vy: 0.0, vz: 0.0, mass: SOLAR_MASS, }; } const bodies = [Sun(), Jupiter(), Saturn(), Uranus(), Neptune()]; function offsetMomentum(): void { let px = 0; let py = 0; let pz = 0; const size = bodies.length; for (let i = 0; i < size; i++) { const body = bodies[i]; const mass = body.mass; px += body.vx * mass; py += body.vy * mass; pz += body.vz * mass; } const body = bodies[0]; body.vx = -px / SOLAR_MASS; body.vy = -py / SOLAR_MASS; body.vz = -pz / SOLAR_MASS; } function advance(dt: number): void { const size = bodies.length; for (let i = 0; i < size; i++) { const bodyi = bodies[i]; let vxi = bodyi.vx; let vyi = bodyi.vy; let vzi = bodyi.vz; for (let j = i + 1; j < size; j++) { const bodyj = bodies[j]; const dx = bodyi.x - bodyj.x; const dy = bodyi.y - bodyj.y; const dz = bodyi.z - bodyj.z; const d2 = dx * dx + dy * dy + dz * dz; const mag = dt / (d2 * Math.sqrt(d2)); const massj = bodyj.mass; vxi = vxi - dx * massj * mag; vyi = vyi - dy * massj * mag; vzi = vzi - dz * massj * mag; const massi = bodyi.mass; bodyj.vx = bodyj.vx + dx * massi * mag; bodyj.vy = bodyj.vy + dy * massi * mag; bodyj.vz = bodyj.vz + dz * massi * mag; } bodyi.vx = vxi; bodyi.vy = vyi; bodyi.vz = vzi; bodyi.x = bodyi.x + dt * vxi; bodyi.y = bodyi.y + dt * vyi; bodyi.z = bodyi.z + dt * vzi; } } function energy(): number { let e = 0; const size = bodies.length; for (let i = 0; i < size; i++) { const bodyi = bodies[i]; e += 0.5 * bodyi.mass * (bodyi.vx * bodyi.vx + bodyi.vy * bodyi.vy + bodyi.vz * bodyi.vz); for (let j = i + 1; j < size; j++) { const bodyj = bodies[j]; const dx = bodyi.x - bodyj.x; const dy = bodyi.y - bodyj.y; const dz = bodyi.z - bodyj.z; const distance = Math.sqrt(dx * dx + dy * dy + dz * dz); e -= (bodyi.mass * bodyj.mass) / distance; } } return e; } export function main() { const n = 1000000; offsetMomentum(); energy(); for (let i = 0; i < n; i++) { advance(0.01); } energy(); } ================================================ FILE: tests/benchmark/nbody_interface.js ================================================ "use strict"; /* The Computer Language Benchmarks Game https://salsa.debian.org/benchmarksgame-team/benchmarksgame/ contributed by Isaac Gouy modified by Andrey Filatkin modified for typescript by Isaac Gouy modified for deno runtime by hanabi1224 */ /* This file is modified base on: * https://github.com/hanabi1224/Programming-Language-Benchmarks/blob/main/bench/algorithm/nbody/6.ts */ // const PI = Math.PI; var PI = 3.141592653589793; var SOLAR_MASS = 4 * PI * PI; var DAYS_PER_YEAR = 365.24; function Jupiter() { return { x: 4.8414314424647209, y: -1.16032004402742839, z: -1.03622044471123109e-1, vx: 1.66007664274403694e-3 * DAYS_PER_YEAR, vy: 7.69901118419740425e-3 * DAYS_PER_YEAR, vz: -6.90460016972063023e-5 * DAYS_PER_YEAR, mass: 9.54791938424326609e-4 * SOLAR_MASS }; } function Saturn() { return { x: 8.34336671824457987, y: 4.12479856412430479, z: -4.03523417114321381e-1, vx: -2.76742510726862411e-3 * DAYS_PER_YEAR, vy: 4.99852801234917238e-3 * DAYS_PER_YEAR, vz: 2.30417297573763929e-5 * DAYS_PER_YEAR, mass: 2.85885980666130812e-4 * SOLAR_MASS }; } function Uranus() { return { x: 1.2894369562139131e1, y: -1.51111514016986312e1, z: -2.23307578892655734e-1, vx: 2.96460137564761618e-3 * DAYS_PER_YEAR, vy: 2.3784717395948095e-3 * DAYS_PER_YEAR, vz: -2.96589568540237556e-5 * DAYS_PER_YEAR, mass: 4.36624404335156298e-5 * SOLAR_MASS }; } function Neptune() { return { x: 1.53796971148509165e1, y: -2.59193146099879641e1, z: 1.79258772950371181e-1, vx: 2.68067772490389322e-3 * DAYS_PER_YEAR, vy: 1.62824170038242295e-3 * DAYS_PER_YEAR, vz: -9.5159225451971587e-5 * DAYS_PER_YEAR, mass: 5.15138902046611451e-5 * SOLAR_MASS }; } function Sun() { return { x: 0.0, y: 0.0, z: 0.0, vx: 0.0, vy: 0.0, vz: 0.0, mass: SOLAR_MASS }; } var bodies = [Sun(), Jupiter(), Saturn(), Uranus(), Neptune()]; function offsetMomentum() { var px = 0; var py = 0; var pz = 0; var size = bodies.length; for (var i = 0; i < size; i++) { var body_1 = bodies[i]; var mass = body_1.mass; px += body_1.vx * mass; py += body_1.vy * mass; pz += body_1.vz * mass; } var body = bodies[0]; body.vx = -px / SOLAR_MASS; body.vy = -py / SOLAR_MASS; body.vz = -pz / SOLAR_MASS; } function advance(dt) { var size = bodies.length; for (var i = 0; i < size; i++) { var bodyi = bodies[i]; var vxi = bodyi.vx; var vyi = bodyi.vy; var vzi = bodyi.vz; for (var j = i + 1; j < size; j++) { var bodyj = bodies[j]; var dx = bodyi.x - bodyj.x; var dy = bodyi.y - bodyj.y; var dz = bodyi.z - bodyj.z; var d2 = dx * dx + dy * dy + dz * dz; var mag = dt / (d2 * Math.sqrt(d2)); var massj = bodyj.mass; vxi = vxi - dx * massj * mag; vyi = vyi - dy * massj * mag; vzi = vzi - dz * massj * mag; var massi = bodyi.mass; bodyj.vx = bodyj.vx + dx * massi * mag; bodyj.vy = bodyj.vy + dy * massi * mag; bodyj.vz = bodyj.vz + dz * massi * mag; } bodyi.vx = vxi; bodyi.vy = vyi; bodyi.vz = vzi; bodyi.x = bodyi.x + dt * vxi; bodyi.y = bodyi.y + dt * vyi; bodyi.z = bodyi.z + dt * vzi; } } function energy() { var e = 0; var size = bodies.length; for (var i = 0; i < size; i++) { var bodyi = bodies[i]; e += 0.5 * bodyi.mass * (bodyi.vx * bodyi.vx + bodyi.vy * bodyi.vy + bodyi.vz * bodyi.vz); for (var j = i + 1; j < size; j++) { var bodyj = bodies[j]; var dx = bodyi.x - bodyj.x; var dy = bodyi.y - bodyj.y; var dz = bodyi.z - bodyj.z; var distance = Math.sqrt(dx * dx + dy * dy + dz * dz); e -= (bodyi.mass * bodyj.mass) / distance; } } return e; } function main() { var n = 1000000; offsetMomentum(); energy(); for (var i = 0; i < n; i++) { advance(0.01); } energy(); } main() ================================================ FILE: tests/benchmark/nbody_interface.ts ================================================ /* The Computer Language Benchmarks Game https://salsa.debian.org/benchmarksgame-team/benchmarksgame/ contributed by Isaac Gouy modified by Andrey Filatkin modified for typescript by Isaac Gouy modified for deno runtime by hanabi1224 */ /* This file is modified base on: * https://github.com/hanabi1224/Programming-Language-Benchmarks/blob/main/bench/algorithm/nbody/6.ts */ // const PI = Math.PI; const PI = 3.141592653589793; const SOLAR_MASS = 4 * PI * PI; const DAYS_PER_YEAR = 365.24; interface NBody { x: number; y: number; z: number; vx: number; vy: number; vz: number; mass: number; } function Jupiter(): NBody { return { x: 4.8414314424647209, y: -1.16032004402742839, z: -1.03622044471123109e-1, vx: 1.66007664274403694e-3 * DAYS_PER_YEAR, vy: 7.69901118419740425e-3 * DAYS_PER_YEAR, vz: -6.90460016972063023e-5 * DAYS_PER_YEAR, mass: 9.54791938424326609e-4 * SOLAR_MASS, }; } function Saturn(): NBody { return { x: 8.34336671824457987, y: 4.12479856412430479, z: -4.03523417114321381e-1, vx: -2.76742510726862411e-3 * DAYS_PER_YEAR, vy: 4.99852801234917238e-3 * DAYS_PER_YEAR, vz: 2.30417297573763929e-5 * DAYS_PER_YEAR, mass: 2.85885980666130812e-4 * SOLAR_MASS, }; } function Uranus(): NBody { return { x: 1.2894369562139131e1, y: -1.51111514016986312e1, z: -2.23307578892655734e-1, vx: 2.96460137564761618e-3 * DAYS_PER_YEAR, vy: 2.3784717395948095e-3 * DAYS_PER_YEAR, vz: -2.96589568540237556e-5 * DAYS_PER_YEAR, mass: 4.36624404335156298e-5 * SOLAR_MASS, }; } function Neptune(): NBody { return { x: 1.53796971148509165e1, y: -2.59193146099879641e1, z: 1.79258772950371181e-1, vx: 2.68067772490389322e-3 * DAYS_PER_YEAR, vy: 1.62824170038242295e-3 * DAYS_PER_YEAR, vz: -9.5159225451971587e-5 * DAYS_PER_YEAR, mass: 5.15138902046611451e-5 * SOLAR_MASS, }; } function Sun(): NBody { return { x: 0.0, y: 0.0, z: 0.0, vx: 0.0, vy: 0.0, vz: 0.0, mass: SOLAR_MASS, }; } const bodies = [Sun(), Jupiter(), Saturn(), Uranus(), Neptune()]; function offsetMomentum(): void { let px = 0; let py = 0; let pz = 0; const size = bodies.length; for (let i = 0; i < size; i++) { const body = bodies[i]; const mass = body.mass; px += body.vx * mass; py += body.vy * mass; pz += body.vz * mass; } const body = bodies[0]; body.vx = -px / SOLAR_MASS; body.vy = -py / SOLAR_MASS; body.vz = -pz / SOLAR_MASS; } function advance(dt: number): void { const size = bodies.length; for (let i = 0; i < size; i++) { const bodyi = bodies[i]; let vxi = bodyi.vx; let vyi = bodyi.vy; let vzi = bodyi.vz; for (let j = i + 1; j < size; j++) { const bodyj = bodies[j]; const dx = bodyi.x - bodyj.x; const dy = bodyi.y - bodyj.y; const dz = bodyi.z - bodyj.z; const d2 = dx * dx + dy * dy + dz * dz; const mag = dt / (d2 * Math.sqrt(d2)); const massj = bodyj.mass; vxi = vxi - dx * massj * mag; vyi = vyi - dy * massj * mag; vzi = vzi - dz * massj * mag; const massi = bodyi.mass; bodyj.vx = bodyj.vx + dx * massi * mag; bodyj.vy = bodyj.vy + dy * massi * mag; bodyj.vz = bodyj.vz + dz * massi * mag; } bodyi.vx = vxi; bodyi.vy = vyi; bodyi.vz = vzi; bodyi.x = bodyi.x + dt * vxi; bodyi.y = bodyi.y + dt * vyi; bodyi.z = bodyi.z + dt * vzi; } } function energy(): number { let e = 0; const size = bodies.length; for (let i = 0; i < size; i++) { const bodyi = bodies[i]; e += 0.5 * bodyi.mass * (bodyi.vx * bodyi.vx + bodyi.vy * bodyi.vy + bodyi.vz * bodyi.vz); for (let j = i + 1; j < size; j++) { const bodyj = bodies[j]; const dx = bodyi.x - bodyj.x; const dy = bodyi.y - bodyj.y; const dz = bodyi.z - bodyj.z; const distance = Math.sqrt(dx * dx + dy * dy + dz * dz); e -= (bodyi.mass * bodyj.mass) / distance; } } return e; } export function main() { const n = 1000000; offsetMomentum(); energy(); for (let i = 0; i < n; i++) { advance(0.01); } energy(); } ================================================ FILE: tests/benchmark/quicksort.js ================================================ "use strict"; /* This file is modified base on: * https://browserbench.org/JetStream/wasm/quicksort.c */ var SORTELEMENTS = 1e5; var maximum = 0; var minimum = 65536; function rand(seed) { seed = (seed * 1309 + 13849) & 65535; return seed; } function initArr(sortList) { var seed = 74755; for (var i = 1; i <= SORTELEMENTS; i++) { sortList[i] = rand(seed); if (sortList[i] > maximum) { maximum = sortList[i]; } else if (sortList[i] < minimum) { minimum = sortList[i]; } } } function quickSort(a, l, r) { var i = l, j = r; var w; var idx = Math.floor((l + r) / 2); var x = a[idx]; do { while (a[i] < x) i++; while (x < a[j]) j--; if (i <= j) { w = a[i]; a[i] = a[j]; a[j] = w; i++; j--; } } while (i <= j); if (l < j) quickSort(a, l, j); if (i < r) quickSort(a, i, r); } function main() { var sortList = new Array(SORTELEMENTS + 1); initArr(sortList); quickSort(sortList, 1, SORTELEMENTS); if (sortList[1] != minimum || sortList[SORTELEMENTS] != maximum) { console.log('Validate result error in quicksort'); } } main() ================================================ FILE: tests/benchmark/quicksort.ts ================================================ /* This file is modified base on: * https://browserbench.org/JetStream/wasm/quicksort.c */ const SORTELEMENTS = 1e5; let maximum = 0; let minimum = 65536; function rand(seed: number) { seed = (seed * 1309 + 13849) & 65535; return seed; } function initArr(sortList: number[]) { const seed = 74755; for (let i = 1; i <= SORTELEMENTS; i++) { sortList[i] = rand(seed); if (sortList[i] > maximum) { maximum = sortList[i]; } else if (sortList[i] < minimum) { minimum = sortList[i]; } } } function quickSort(a: number[], l: number, r: number) { let i = l, j = r; let w: number; const idx = Math.floor((l + r) / 2); const x = a[idx]; do { while (a[i] < x) i++; while (x < a[j]) j--; if (i <= j) { w = a[i]; a[i] = a[j]; a[j] = w; i++; j--; } } while (i <= j); if (l < j) quickSort(a, l, j); if (i < r) quickSort(a, i, r); } export function main() { const sortList = new Array(SORTELEMENTS + 1); initArr(sortList); quickSort(sortList, 1, SORTELEMENTS); if (sortList[1] != minimum || sortList[SORTELEMENTS] != maximum) { console.log('Validate result error in quicksort'); } } ================================================ FILE: tests/benchmark/quicksort_float.js ================================================ "use strict"; /* This file is modified base on: * https://browserbench.org/JetStream/wasm/quicksort.c */ var SORTELEMENTS = 1e5; var maximum = 0; var minimum = 65536; function rand(seed) { seed = (seed * 1309 + 13849) & 65535; return seed; } function initArr(sortList) { var seed = 74755; for (var i = 1; i <= SORTELEMENTS; i++) { var val = rand(seed); sortList[i] = val + val / 65536; if (sortList[i] > maximum) { maximum = sortList[i]; } else if (sortList[i] < minimum) { minimum = sortList[i]; } } } function quickSort(a, l, r) { var i = l, j = r; var w; var idx = Math.floor((l + r) / 2); var x = a[idx]; do { while (a[i] < x) i++; while (x < a[j]) j--; if (i <= j) { w = a[i]; a[i] = a[j]; a[j] = w; i++; j--; } } while (i <= j); if (l < j) quickSort(a, l, j); if (i < r) quickSort(a, i, r); } function main() { var sortList = new Array(SORTELEMENTS + 1); initArr(sortList); quickSort(sortList, 1, SORTELEMENTS); if (sortList[1] != minimum || sortList[SORTELEMENTS] != maximum) { console.log('Validate result error in quicksort'); } } main() ================================================ FILE: tests/benchmark/quicksort_float.ts ================================================ /* This file is modified base on: * https://browserbench.org/JetStream/wasm/quicksort.c */ const SORTELEMENTS = 1e5; let maximum = 0; let minimum = 65536; function rand(seed: number) { seed = (seed * 1309 + 13849) & 65535; return seed; } function initArr(sortList: number[]) { const seed = 74755; for (let i = 1; i <= SORTELEMENTS; i++) { const val = rand(seed); sortList[i] = val + val / 65536; if (sortList[i] > maximum) { maximum = sortList[i]; } else if (sortList[i] < minimum) { minimum = sortList[i]; } } } function quickSort(a: number[], l: number, r: number) { let i = l, j = r; let w: number; const idx = Math.floor((l + r) / 2); const x = a[idx]; do { while (a[i] < x) i++; while (x < a[j]) j--; if (i <= j) { w = a[i]; a[i] = a[j]; a[j] = w; i++; j--; } } while (i <= j); if (l < j) quickSort(a, l, j); if (i < r) quickSort(a, i, r); } export function main() { const sortList = new Array(SORTELEMENTS + 1); initArr(sortList); quickSort(sortList, 1, SORTELEMENTS); if (sortList[1] != minimum || sortList[SORTELEMENTS] != maximum) { console.log('Validate result error in quicksort'); } } ================================================ FILE: tests/benchmark/run.sh ================================================ # Copyright (C) 2023 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #!/bin/bash temp_folder="/tmp" benchmark_dir=$(cd "$(dirname "$0")" && pwd) benchmarks=$(ls $benchmark_dir) ts2wasm_script=$benchmark_dir/../../build/cli/ts2wasm.js iwasm_gc=$benchmark_dir/../../runtime-library/build/iwasm_gc wamrc=$benchmark_dir/../../runtime-library/deps/wamr-gc/wamr-compiler/build/wamrc optimize_level=3 QJS_CMD=`which qjs` if [ ! -z "$QJS_CMD" ]; then qjs="qjs" elif [ -n "$QJS_PATH" ]; then qjs="$QJS_PATH" else default_qjs_path=/usr/local/bin/qjs if [ -f "$default_qjs_path" ]; then qjs="$default_qjs_path" else echo "Error: QJS_PATH is not defined, and no default qjs path is provided." exit 1 fi fi ts_times=() js_times=() aot_times=() prefixs=() tmp_total_second=0 process_time_info() { time_info=$(cat time_output.txt) echo "$time_info" real_time=$(echo "$time_info" | grep "real" | awk '{print $2}') minutes=$(echo "$real_time" | cut -dm -f1) seconds=$(echo "$real_time" | cut -dm -f2 | sed 's/s//') echo "" tmp_total_second=$(bc <<< "$minutes * 60 + $seconds") } for benchmark in $benchmarks; do if [ -f "$benchmark" ]; then filename=$(basename "$benchmark") prefix="${filename%%.*}" extension="${filename##*.}" if [ "$prefix" = "merkletrees" ]; then continue; fi if [ "$extension" = "ts" ]; then prefixs+=($prefix) echo "Run $prefix benchmark by WAMR interpreter:" node $ts2wasm_script $benchmark_dir/$benchmark --opt ${optimize_level} --output $prefix.wasm > tmp.txt { time $iwasm_gc --gc-heap-size=52428800 -f main $prefix.wasm; } 2> time_output.txt process_time_info ts_times+=($tmp_total_second) echo "Run $prefix benchmark by WAMR aot:" $wamrc --enable-gc -o $prefix.aot $prefix.wasm > tmp.txt { time $iwasm_gc --gc-heap-size=52428800 -f main $prefix.aot; } 2> time_output.txt process_time_info aot_times+=($tmp_total_second) elif [ "$extension" = "js" ]; then echo "Run $prefix benchmark by qjs:" { time $qjs $benchmark_dir/$benchmark; } 2> time_output.txt process_time_info js_times+=($tmp_total_second) fi fi done rm -rf *.txt rm -rf *.wasm rm -rf *.wat rm -rf *.aot for ((i = 0; i < ${#ts_times[@]}; i++)); do ts_time=${ts_times[$i]} js_time=${js_times[$i]} aot_time=${aot_times[$i]} ratio=$(bc -l <<< "scale=2; $ts_time / $js_time") formatted_result=$(printf "%.2f" "$ratio") echo "Time ratio for ${prefixs[$i]} benchmark (WAMR_interpreter/qjs): $formatted_result" ratio_aot=$(bc -l <<< "scale=2; $aot_time / $js_time") formatted_result_aot=$(printf "%.2f" "$ratio_aot") echo "Time ratio for ${prefixs[$i]} benchmark (WAMR_aot/qjs): $formatted_result_aot" echo "" done ================================================ FILE: tests/benchmark/run_benchmark.js ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import fs from 'fs'; import path from 'path'; import { execSync } from 'child_process'; import { performance } from 'perf_hooks'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; const __filename = fileURLToPath(import.meta.url); const benchmark_dir = dirname(__filename); const benchmarks = fs.readdirSync(benchmark_dir); const ts2wasm_script = path.join(benchmark_dir, '../../build/cli/ts2wasm.js'); const iwasm_gc = path.join(benchmark_dir, '../../runtime-library/build/iwasm_gc'); const default_qjs = path.join(benchmark_dir, '../../runtime-library/deps/quickjs/qjs'); const wamrc = path.join(benchmark_dir, '../../runtime-library/deps/wamr-gc/wamr-compiler/build/wamrc'); const optimize_level = 3; const validate_res_error = 'Validate result error'; function print_help() { console.log(`Usage: node run_benchmark.js [options]`); console.log(`Options:`); console.log(` --no-clean=true|false`); console.log(` --times=NUM`); console.log(` --gc-heap=NUM`); console.log(` --benchmarks=NAME1,NAME2,...`); console.log(` --runtimes=NAME1,NAME2,...`); console.log(` --help`); console.log(`Example:`); console.log(` node run_benchmark.js --no-clean=true --times=10 --gc-heap=40960000 --benchmarks=mandelbrot,binarytrees_class --runtimes=wamr-interp,qjs`); process.exit(0); } function parseArguments(rawArgs) { const args = {}; rawArgs.forEach(arg => { if (arg === '--help' || arg === 'help' || arg === 'h') { print_help(); } const [key, value] = arg.split('='); args[key] = value; }); return args; } const args = parseArguments(process.argv.slice(2)); const shouldClean = args['--no-clean'] ? false : true; const multirun = args['--times'] ? parseInt(args['--times']) : 1; const wamr_stack_size = args['--stack-size'] ? parseInt(args['--stack-size']) : 40960000; const wamr_gc_heap = args['--gc-heap'] ? parseInt(args['--gc-heap']) : 40960000; const specifed_benchmarks = args['--benchmarks'] ? args['--benchmarks'].split(',') : null; const specified_runtimes = args['--runtimes'] ? args['--runtimes'].split(',') : null; const warm_up_times = args['--warmup'] ? parseInt(args['--warmup']) : 0; const default_gc_size_option = `--gc-heap-size=${wamr_gc_heap}` const stack_size_option = `--stack-size=${wamr_stack_size}` let qjs; try { qjs = execSync('which qjs').toString().trim(); } catch (error) { if (process.env.QJS_PATH) { qjs = process.env.QJS_PATH; } else { const default_qjs_path = '/usr/local/bin/qjs'; if (fs.existsSync(default_qjs_path)) { qjs = default_qjs_path; } else { if (fs.existsSync(default_qjs)) { qjs = default_qjs; } else { console.error("Error: QJS_PATH is not defined, and no default qjs path is provided."); process.exit(1); } } } } let node_cmd; try { node_cmd = execSync('which node').toString().trim(); } catch (error) { if (process.env.NODE_PATH) { node_cmd = process.env.NODE_PATH; } else { const default_node_path = '/usr/local/bin/node'; if (fs.existsSync(default_node_path)) { node_cmd = default_node_path; } else { console.error("Error: NODE_PATH is not defined, and no default node path is provided."); process.exit(1); } } } let wamr_interp_times = []; let qjs_js_times = []; let wamr_aot_times = []; let v8_js_times = []; let prefixs = []; let benchmark_options = { 'merkletrees': { skip: true }, 'mandelbrot': { wamr_option: [default_gc_size_option] }, 'mandelbrot_i32': { wamr_option: [default_gc_size_option] }, 'binarytrees_class': { wamr_option: [default_gc_size_option] }, 'binarytrees_interface': { wamr_option: [default_gc_size_option] }, 'quicksort': { wamr_option: [stack_size_option, default_gc_size_option] }, 'quicksort_float': { wamr_option: [stack_size_option, default_gc_size_option] }, } function collect_benchmark_options(options) { if (options == undefined) { return ''; } return options.join(' '); } console.log(`\x1b[33m======================== options ========================\x1b[0m`); console.log(`QJS_PATH: ${qjs}`); console.log(`NODE_PATH: ${node_cmd}`); console.log(`strategy: run ${multirun} times and get average`); console.log(`clean generated files: ${shouldClean ? 'true' : 'false'}`); console.log(`\x1b[33m======================== running ========================\x1b[0m`); function run_multiple_times(cmd) { let elapsed; let elapse_arr = []; try { for (let i = 0; i < warm_up_times; i++) { execSync(cmd); } for (let i = 0; i < multirun; i++) { let start = performance.now(); let ret = execSync(cmd); let end = performance.now(); elapsed = (end - start); elapse_arr.push(elapsed); ret = ret.toString().trim(); if (ret.startsWith(validate_res_error)) { throw new Error(ret); } } } catch (e) { console.log('') if (e.status) { console.log(`\x1b[31mExit Code: ${e.status}\x1b[0m`); } console.log(`\x1b[31m${e.message}\x1b[0m`); if (e.stdout) { console.log(`\x1b[31m${e.stdout.toString()}\x1b[0m`); } process.exit(1); } elapsed = elapse_arr.reduce((a, b) => a + b, 0) / elapse_arr.length; return elapsed; } let executed_benchmarks = 0; for (let benchmark of benchmarks) { let filename = path.basename(benchmark); let prefix = path.basename(filename, path.extname(filename)); let extension = path.extname(filename).slice(1); let js_file = `${prefix}.js`; let elapsed; if (extension != 'ts') continue; if (!fs.existsSync(`${prefix}.js`)) continue; if (benchmark_options[prefix]?.skip) { console.log(`\x1b[33mSkip ${prefix} benchmark.\x1b[0m`); continue; } if (specifed_benchmarks && !specifed_benchmarks.includes(prefix)) { console.log(`\x1b[33mSkip ${prefix} benchmark due to argument filter.\x1b[0m`); continue; } console.log(`\x1b[36m################### ${prefix} ###################\x1b[0m`); prefixs.push(prefix); console.log(`Compiling ${prefix} benchmark:`); execSync(`node ${ts2wasm_script} ${filename} --opt ${optimize_level} --output ${prefix}.wasm > tmp.txt`); execSync(`${wamrc} --enable-gc -o ${prefix}.aot ${prefix}.wasm > tmp.txt`); if (specified_runtimes && !specified_runtimes.includes('wamr-interp')) { console.log(`\x1b[33mSkip WAMR interpreter due to argument filter.\x1b[0m`); } else { process.stdout.write(`WAMR interpreter ... \t`); elapsed = run_multiple_times(`${iwasm_gc} ${collect_benchmark_options(benchmark_options[prefix]?.wamr_option)} -f main ${prefix}.wasm`); wamr_interp_times.push(elapsed); console.log(`${elapsed.toFixed(2)}ms`); } if (specified_runtimes && !specified_runtimes.includes('wamr-aot')) { console.log(`\x1b[33mSkip WAMR AoT due to argument filter.\x1b[0m`); } else { process.stdout.write(`WAMR AoT ... \t\t`); elapsed = run_multiple_times(`${iwasm_gc} ${collect_benchmark_options(benchmark_options[prefix]?.wamr_option)} -f main ${prefix}.aot`); wamr_aot_times.push(elapsed); console.log(`${elapsed.toFixed(2)}ms`); } if (specified_runtimes && !specified_runtimes.includes('qjs')) { console.log(`\x1b[33mSkip QuickJS due to argument filter.\x1b[0m`); } else { process.stdout.write(`QuickJS ... \t\t`); elapsed = run_multiple_times(`${qjs} ${js_file}`); qjs_js_times.push(elapsed); console.log(`${elapsed.toFixed(2)}ms`); } if (specified_runtimes && !specified_runtimes.includes('node')) { console.log(`\x1b[33mSkip Node due to argument filter.\x1b[0m`); } else { process.stdout.write(`Node ... \t\t`); elapsed = run_multiple_times(`${node_cmd} ${js_file}`); v8_js_times.push(elapsed); console.log(`${elapsed.toFixed(2)}ms`); } executed_benchmarks++; } if (shouldClean) { execSync(`rm -f *.wasm`); execSync(`rm -f *.aot`); execSync(`rm -f tmp.txt`); } console.log(`\x1b[32m====================== results ======================\x1b[0m`); let results = []; for (let i = 0; i < executed_benchmarks; i++) { let wamr_interp_time = wamr_interp_times[i]; let qjs_js_time = qjs_js_times[i]; let wamr_aot_time = wamr_aot_times[i]; let v8_js_time = v8_js_times[i]; let r = { benchmark: prefixs[i] } if (wamr_interp_time) { r['WAMR_interpreter'] = wamr_interp_time.toFixed(2) + 'ms'; } if (wamr_aot_time) { r['WAMR_aot'] = wamr_aot_time.toFixed(2) + 'ms'; } if (qjs_js_time) { r['QuickJS'] = qjs_js_time.toFixed(2) + 'ms'; } if (v8_js_time) { r['Node'] = v8_js_time.toFixed(2) + 'ms'; } if (wamr_interp_time && qjs_js_time) { let ratio = wamr_interp_time / qjs_js_time; let formatted_result = ratio.toFixed(2); r['WAMR_interpreter/qjs'] = formatted_result; } if (wamr_aot_time && qjs_js_time) { let ratio_aot = wamr_aot_time / qjs_js_time; let formatted_result_aot = ratio_aot.toFixed(2); r['WAMR_aot/qjs'] = formatted_result_aot; } if (wamr_interp_time && v8_js_time) { let ratio = wamr_interp_time / v8_js_time; let formatted_result = ratio.toFixed(2); r['WAMR_interpreter/node'] = formatted_result; } if (wamr_aot_time && v8_js_time) { let ratio_aot = wamr_aot_time / v8_js_time; let formatted_result_aot = ratio_aot.toFixed(2); r['WAMR_aot/node'] = formatted_result_aot; } results.push(r); } console.table(results); ================================================ FILE: tests/benchmark/run_benchmark_on_chrome.html ================================================ run benchmark

benchmark wasm result(ms) js result(ms) wasm result / js result
any_basic_type_access 0 0 NAN
array_access 0 0 NAN
array_access_i32 0 0 NAN
binarytrees_class 0 0 NAN
class_access 0 0 NAN
class_allocation 0 0 NAN
fibonacci 0 0 NAN
interface_access_field_fastpath 0 0 NAN
interface_access_method_fastpath 0 0 NAN
mandelbrot 0 0 NAN
mandelbrot_i32 0 0 NAN
nbody_class 0 0 NAN
nbody_interface 0 0 NAN
quicksort 0 0 NAN
quicksort_float 0 0 NAN
spectral_norm 0 0 NAN
spectral_norm_i32 0 0 NAN

1. WebAssembly and JavaScript both warm-up n times before measuring the execution time

2. _i32" suffix means using i32 type in WebAssembly, no effect when building to JavaScript

3. The data varies from run to run, the data shown on the table is the average of `runTimes` runs

================================================ FILE: tests/benchmark/run_benchmark_on_chrome.js ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { importObject, setWasmMemory } from '../../tools/validate/run_module/import_object.js'; const compile_output_dir = './compile_output/'; export async function run_wasm_file(fileName, warmupTimes, runTimes) { return fetch(compile_output_dir.concat(fileName, '.wasm')) .then((response) => response.arrayBuffer()) .then((bytes) => WebAssembly.instantiate(bytes, importObject)) .then((results) => { const exports = results.instance.exports; setWasmMemory(exports.default); const startFunc = exports._entry; const funcName = 'main'; const exportedFunc = exports[funcName]; startFunc(); if (warmupTimes) { for (let i = 0; i < parseInt(warmupTimes); i++) { exportedFunc(); } } let run_time_total = 0; if (!runTimes) { runTimes = 1; } for (let i = 0; i < parseInt(runTimes); i++) { const start_time = performance.now(); const res = exportedFunc(); const end_time = performance.now(); run_time_total += end_time - start_time; if (typeof res !== 'object' || res === null) { console.log(`${fileName}.wasm result is : ${res}`); } else { console.log(`${fileName}.wasm result is object`); } } const run_time_average = run_time_total / runTimes; const cell1 = document.getElementById(`${fileName}_1`); cell1.textContent = run_time_average.toFixed(2); const cell2_value = document.getElementById(`${fileName}_2`).textContent; const cell3 = document.getElementById(`${fileName}_3`); cell3.textContent = (run_time_average / cell2_value).toFixed(2); }) .catch((error)=> { console.error(`${fileName}.wasm occurs error`); }) } ================================================ FILE: tests/benchmark/spectral_norm.js ================================================ "use strict"; /* The Computer Language Benchmarks Game https://salsa.debian.org/benchmarksgame-team/benchmarksgame/ contributed by Isaac Gouy */ /* This file is from: * https://benchmarksgame-team.pages.debian.net/benchmarksgame/program/spectralnorm-typescript-1.html */ function approximate(n) { var u = new Array(n), v = new Array(n); for (var i = 0; i < n; ++i) { u[i] = 1.0; } for (var i = 0; i < 10; ++i) { multiplyAtAv(n, u, v); multiplyAtAv(n, v, u); } var vBv = 0.0, vv = 0.0; for (var i = 0; i < 10; ++i) { vBv += u[i] * v[i]; vv += v[i] * v[i]; } return Math.sqrt(vBv / vv); } function a(i, j) { return 1.0 / (((i + j) * (i + j + 1)) / 2 + i + 1); } function multiplyAv(n, v, av) { for (var i = 0; i < n - 1; ++i) { av[i] = 0.0; for (var j = 0; j < n - 1; ++j) { av[i] = av[i] + a(i, j) * v[j]; } } } function multiplyAtv(n, v, atv) { for (var i = 0; i < n - 1; ++i) { atv[i] = 0.0; for (var j = 0; j < n - 1; ++j) { atv[i] = atv[i] + a(j, i) * v[j]; } } } function multiplyAtAv(n, v, atAv) { var u = new Array(n); multiplyAv(n, v, u); multiplyAtv(n, u, atAv); } function main() { var n = 2000; approximate(n); } main() ================================================ FILE: tests/benchmark/spectral_norm.ts ================================================ /* The Computer Language Benchmarks Game https://salsa.debian.org/benchmarksgame-team/benchmarksgame/ contributed by Isaac Gouy */ /* This file is from: * https://benchmarksgame-team.pages.debian.net/benchmarksgame/program/spectralnorm-typescript-1.html */ function approximate(n: number): number { const u: number[] = new Array(n), v: number[] = new Array(n); for (let i = 0; i < n; ++i) { u[i] = 1.0; } for (let i = 0; i < 10; ++i) { multiplyAtAv(n, u, v); multiplyAtAv(n, v, u); } let vBv = 0.0, vv = 0.0; for (let i = 0; i < 10; ++i) { vBv += u[i] * v[i]; vv += v[i] * v[i]; } return Math.sqrt(vBv / vv); } function a(i: number, j: number): number { return 1.0 / (((i + j) * (i + j + 1)) / 2 + i + 1); } function multiplyAv(n: number, v: number[], av: number[]) { for (let i = 0; i < n - 1; ++i) { av[i] = 0.0; for (let j = 0; j < n - 1; ++j) { av[i] = av[i] + a(i, j) * v[j]; } } } function multiplyAtv(n: number, v: number[], atv: number[]) { for (let i = 0; i < n - 1; ++i) { atv[i] = 0.0; for (let j = 0; j < n - 1; ++j) { atv[i] = atv[i] + a(j, i) * v[j]; } } } function multiplyAtAv(n: number, v: number[], atAv: number[]) { const u: number[] = new Array(n); multiplyAv(n, v, u); multiplyAtv(n, u, atAv); } export function main() { const n = 2000; approximate(n); } ================================================ FILE: tests/benchmark/spectral_norm_i32.js ================================================ "use strict"; /* The Computer Language Benchmarks Game https://salsa.debian.org/benchmarksgame-team/benchmarksgame/ contributed by Isaac Gouy */ function approximate(n) { var u = new Array(n), v = new Array(n); for (var i = 0; i < n; ++i) { u[i] = 1.0; } for (var i = 0; i < 10; ++i) { multiplyAtAv(n, u, v); multiplyAtAv(n, v, u); } var vBv = 0.0, vv = 0.0; for (var i = 0; i < 10; ++i) { vBv += u[i] * v[i]; vv += v[i] * v[i]; } return Math.sqrt(vBv / vv); } function a(i, j) { return 1.0 / (((i + j) * (i + j + 1)) / 2 + i + 1); } function multiplyAv(n, v, av) { for (var i = 0; i < n - 1; ++i) { av[i] = 0.0; for (var j = 0; j < n - 1; ++j) { av[i] = av[i] + a(i, j) * v[j]; } } } function multiplyAtv(n, v, atv) { for (var i = 0; i < n - 1; ++i) { atv[i] = 0.0; for (var j = 0; j < n - 1; ++j) { atv[i] = atv[i] + a(j, i) * v[j]; } } } function multiplyAtAv(n, v, atAv) { var u = new Array(n); multiplyAv(n, v, u); multiplyAtv(n, u, atAv); } function main() { var n = 2000; approximate(n); } main() ================================================ FILE: tests/benchmark/spectral_norm_i32.ts ================================================ /* The Computer Language Benchmarks Game https://salsa.debian.org/benchmarksgame-team/benchmarksgame/ contributed by Isaac Gouy */ /* This file is from: * https://benchmarksgame-team.pages.debian.net/benchmarksgame/program/spectralnorm-typescript-1.html */ type i32 = number; function approximate(n: i32): number { const u: number[] = new Array(n as number), v: number[] = new Array(n as number); for (let i: i32 = 0; i < n; ++i) { u[i] = 1.0; } for (let i: i32 = 0; i < 10; ++i) { multiplyAtAv(n, u, v); multiplyAtAv(n, v, u); } let vBv = 0.0, vv = 0.0; for (let i: i32 = 0; i < 10; ++i) { vBv += u[i] * v[i]; vv += v[i] * v[i]; } return Math.sqrt(vBv / vv); } function a(i: number, j: number): number { return 1.0 / (((i + j) * (i + j + 1)) / 2 + i + 1); } function multiplyAv(n: i32, v: number[], av: number[]) { for (let i: i32 = 0; i < n - 1; ++i) { av[i] = 0.0; for (let j: i32 = 0; j < n - 1; ++j) { av[i] = av[i] + a(i, j) * v[j]; } } } function multiplyAtv(n: i32, v: number[], atv: number[]) { for (let i: i32 = 0; i < n - 1; ++i) { atv[i] = 0.0; for (let j: i32 = 0; j < n - 1; ++j) { atv[i] = atv[i] + a(j, i) * v[j]; } } } function multiplyAtAv(n: i32, v: number[], atAv: number[]) { const u: number[] = new Array(n as number); multiplyAv(n, v, u); multiplyAtv(n, u, atAv); } export function main() { const n = 2000; approximate(n); } ================================================ FILE: tests/samples/add_shorthand_prop.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ type AttrValue = string | number | boolean interface AttrObject { [key: string]: AttrValue } interface StringObject { [key: string]: string } interface NodeInfo { type: string attr?: AttrObject classList?: Array events?: { [key: string]: StringObject } children?: Array } export function buildNode(): NodeInfo { let type: string = "awesome"; const nodemap: NodeInfo = { type } console.log(nodemap.type); // awesome return nodemap // ref.struct } ================================================ FILE: tests/samples/any_as_string.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ function test(a?: string) { if (a) { let b: string = a as string; console.log(b) } } export function any_as_string() { test('hello world'); // hello world } ================================================ FILE: tests/samples/any_binary_operation.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function addAnyAny() { let a: any = 1; let b: any = 2; let c: any = a + b; return c as number; } export function addNumberAnyInBinaryExpr() { let a: any = 1; let b: any; let c: any = 100; b = (a + 1) * c; return b as number; } export function addNumberAnyInMulExpr() { let a: any = 1; let c: any; c = 2 + a + 6; return c as number; } export function anyCmp() { const a: any = 10, b: any = 10, b2: any = 11; const c: any = false, d: any = true, d2: any = false, e: any = '123', f: any = '123', f2: any = '234'; console.log(c === d); // false console.log(c === d2); // true console.log(a === b); // true console.log(a === b2); // false console.log(a <= b); // true console.log(a > b); // false console.log(e > f); // false console.log(e < f2); // true console.log(e === f); // true console.log(e != f); // false console.log(e === f2); // false } export function anyNullUndefinedCmp() { const n1: any = null, n2: any = null; const u1: any = undefined, u2: any = undefined; console.log(n1 == n2); // true console.log(n1 == u1); // false console.log(u1 == u2); // true console.log(u1 === undefined); // true console.log(u1 !== undefined); // false console.log(n2 === null); // true console.log(n2 !== null); // false } export function anyMinusAny() { let a: any = 10; let b: any = 5; let c: any = a - b; console.log(c); } export function anyDivideAny() { let a: any = 10; let b: any = 5; let c: any = a / b; console.log(c); } export function anyMultiplyAny() { let a: any = 10; let b: any = 5; let c: any = a * b; console.log(c); } export function anyModAny() { let a: any = 10; let b: any = 5; let c: any = a % b; console.log(c); } export function addAnyInBinaryExpr() { const str1: any = 'str11'; let str2: any; const str3: any = 'str33'; str2 = str1 + 'a' + str3; console.log(str2); } export function anyCmpNum() { let a: any = 10; if (a > 9) { console.log('Greater than 9'); } else { console.log('Less than 9'); } if(11 > a) { console.log('Less than 11'); } else { console.log('Greater than 11'); } } ================================================ FILE: tests/samples/any_box_any.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function boxAny() { let a: any = 1; let c: any; c = a; return c as number; } ================================================ FILE: tests/samples/any_box_array.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function boxEmptyArr() { let a: any = []; console.log(a); // [] } export function setArrElem() { let a: any = [10]; a[0] = 100; console.log(a); // [100] } export function getArrElem() { let a: any = [10]; const b = a[0]; console.log(b); // 10 } export function boxNestedArr() { let arr: any = [1, [1, 2]]; console.log(arr[1][1]); } ================================================ FILE: tests/samples/any_box_boolean.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function boxBooleanWithVarStmt() { let a: any = true; return a as boolean; } export function boxBooleanWithBinaryExpr() { let a: any; a = true; return a as boolean; } ================================================ FILE: tests/samples/any_box_interface.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ interface I { x: number; y: boolean; } export function boxInterface() { const i: I = { x: 1, y: true }; const a: any = i; const c = (a as I).y; return c; } ================================================ FILE: tests/samples/any_box_null.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function boxNull() { let a: any = null; return a; } ================================================ FILE: tests/samples/any_box_number.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function boxNumberWithVarStmt() { let a: any = 1; return a as number; } export function boxNumberWithBinaryExpr() { let a: any; a = 1; return a as number; } ================================================ FILE: tests/samples/any_box_obj.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function boxEmptyObj() { let a: any = {}; return a; } export function boxObjWithNumberProp() { let obj: any = { a: 1, }; return obj.a as number; } export function boxObjWithBooleanProp() { let obj: any; obj = { c: true, }; return obj.c as boolean; } export function boxNestedObj() { let obj: any; obj = { a: 1, c: true, d: { e: 1, }, }; return obj.d.e as number; } class A { x = '10'; constructor(xx: string) { this.x = xx; } } function test(str: string) { const a1: any = new A(str); return a1; } export function anyPointToObj() { let b = new A('100'); for (let i = 0; i < 100; i++) { b = test('123') as A; } return b.x; } function foo(param: any) { for (let key in param) { if(key == 'children') { console.log(typeof param[key]); } else { console.log(`${key}: ${param[key]}`); } } } export function boxObjWithProps() { const a: any = { tag: "a", x: 1, children: [ { tag: 'child0', text: 'string: 1', }, { tag: 'child1', text: 'string: 2', } ] }; const b: any = { children: [ { tag: 'child0', text: 'string: 3', }, { tag: 'child1', text: 4, } ] }; foo(a); console.log(b.children[0].text); console.log(b.children[1].text); } ================================================ FILE: tests/samples/any_box_obj_as_return_value.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ function getAnyObj(): any { return { a: 1 }; } export function getObjPropFromReturnValue() { const obj: any = getAnyObj(); console.log(obj.a); } ================================================ FILE: tests/samples/any_box_string.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function boxStringWithVarStmt() { let a: any = 'hello'; return a; } export function boxStringWithBinaryExpr() { let a: any; a = 'hello'; return a; } export function stringPlusAnyString(){ let str1: string = 'string1'; let str2: string = 'string2'; let str3: string = 'string3'; str2 = str2 + str1; console.log(str2); str3 = str3 + str2; console.log(str3); } ================================================ FILE: tests/samples/any_box_undefind.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function boxUndefined() { let a: any = undefined; return a; } ================================================ FILE: tests/samples/any_cast_class.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A { x: number; constructor(xx: number) { this.x = xx; } } function helper(a: any) { const b: A = a; return b.x; } let temp_a = new A(2023); console.log(helper(temp_a)) ================================================ FILE: tests/samples/any_func_call.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function anyFuncCallWithNumber() { const fn1: any = (a: number): number => { return a; }; const fn2: any = (a: number): number => { return a + 100; }; const a1 = fn1(20); console.log(a1); const a2 = fn2(10) console.log(a2); } function funcWithBoolean(a: boolean) { if (a) { return 10; } return 11; } export function anyFuncCallWithBoolean() { const fn1: any = (a: boolean): boolean => { return a; }; const fn2: any = funcWithBoolean; const a1 = fn1(true); console.log(a1); const a2 = fn2(false); console.log(a2); } export function anyFuncCallWithString() { const fn1: any = (): string => { return 'hi'; }; const fn2: any = (a: string): string => { return a + (', world'); }; const a1 = fn1(); console.log(a1); const a2 = fn2('hello'); console.log(a2); } export function anyFuncCallWithAny() { const fn1: any = (a: any): number => { return 100; }; const fn2: any = (a: any): any => { return a; }; const a = fn1(8); console.log(a); const b = fn2('world'); console.log(b); } export function anyFuncCallWithFunc() { const fn: any = (a: ()=>number): ()=>number => { return a; }; const a = fn(()=> {return 8}); const b = a(); console.log(b); } class A { x = 3; y = true; } export function anyFuncCallWithClass() { const fn: any = (a: A): A => { return a; }; const obj: A = new A(); const a = fn(obj); console.log(a.x); console.log(a.y); a.x = 1; console.log(a.x); } export function anyFuncCallWithObj_static() { const fn: any = (a: {x: number, y:boolean}):{x: number, y:boolean} => { return a; }; const obj = {x:3, y:true}; const a = fn(obj); console.log(a.x); console.log(a.y); a.x = 1; console.log(a.x); } interface I { x: number; y: boolean; } export function anyFuncCallWithInfc_class() { const fn: any = (a: I): I => { return a; }; const obj:I = new A(); const a = fn(obj); console.log(a.x); console.log(a.y); a.x = 1; console.log(a.x); } export function anyFuncCallWithInfc_obj() { const fn: any = (a: I): I => { return a; }; const obj:I = {x:3, y:true}; const a = fn(obj); console.log(a.x); console.log(a.y); a.x = 1; console.log(a.x); } export function anyFuncCallWithArray_static() { const fn: any = (a: number[]): number[] => { return a; }; const arr = [9, 6]; const a = fn(arr); const b = a[0]; console.log(b); a[1] = 10; console.log(a[1]); const len = a.length; console.log(len); } export function anyFuncCallWithArray_extref() { const fn: any = (a: number[]): number[] => { return a; }; const arr = [9, 6]; const arr_any: any = arr; const a = fn(arr_any); const b = a[0]; console.log(b); a[1] = 10; console.log(a[1]); const len = a.length; console.log(len); } export function anyFuncCallInMap() { const m:any = new Map(); m.set(1, funcWithBoolean); const fn = m.get(1); const res = fn(true); console.log(res); } class Page { index = 0; log(): void { console.log('Page class'); } increase() { this.index++; console.log(this.index); } } export function anyFuncCallWithCast() { const map: any = new Map(); const fn = () => { return new Page(); }; map.set('Page', fn); const a = map.get('Page'); const b = a() as Page; b.log(); b.increase(); } export function anyFuncCallWithNoCast() { const map: any = new Map(); const fn = () => { return new Page(); }; map.set('Page', fn); const a = map.get('Page'); const b = a(); b.log(); b.increase(); } class A1 { test() { console.log(1); } } class B1 { a?: A1 = new A1(); fun() { if (this.a) { this.a.test(); } } } export function unionFuncCall() { const b = new B1(); b.fun(); } ================================================ FILE: tests/samples/any_nested_literal_value.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function arrayLiteralInObjLiteral() { const obj: any = { a: 'hi', b: [ { a: 'world', b: [ { a: { a: [ [100] ], } } ], } ] } console.log(obj.b[0].b[0].a.a[0][0]); } export function objLiteralInArrayLiteral() { const arr: any = [ { a: 'hi', b: [ { a: [ [ [ { a: { a: 'world', } } ] ] ], b: [100], }, ], }, ]; console.log(arr[0].b[0].a[0][0][0].a.a); } ================================================ FILE: tests/samples/any_obj_prop_get.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function getProp() { let obj: any = {}; obj.a = 1; obj.length = 4; const b = obj.length; return b as number; } ================================================ FILE: tests/samples/any_obj_prop_get_as_string.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ declare function load(path: string): any; class Record { private _entry: string = ''; init(info: any): void { if (info.entry) { let entryStr: string = info.entry as string; } } } class App { init(): void { let source: any = load('sourcePath'); let record: Record = new Record(); if (source.router) { record.init(source.router); } } } export function test() { let app = new App(); app.init(); } ================================================ FILE: tests/samples/any_obj_prop_set.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function setUnExistProp() { let obj: any = {}; obj.a = 1; obj.length = 4; return obj.length as number; } export function setExistProp() { let obj: any = { a: 1 }; obj.a = 2; return obj.a as number; } ================================================ FILE: tests/samples/any_operate_with_static.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function addAnyAndNumber() { let a: any = 1; let b = 2; let c: any = a + b; return c + b; } ================================================ FILE: tests/samples/array_concat.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function array_concat_number() { let array1: number[] = [1, 2]; let length: number = array1.concat(3, 4).length; return length; // 4 } export function array_concat_boolean() { let array1: boolean[] = [true, false]; let length: number = array1.concat(true, false).length; return length; // 4 } export function array_concat_string() { let array1: string[] = ['hello', 'world']; let length: number = array1.concat('hello', 'world').length; return length; // 4 } class A { x: string = 'xxx' } class B extends A { y: number = 1 } export function array_concat_class() { let array1: A[] = [new A(), new A()]; let b : A = new B(); let length: number = array1.concat(new A(), new A(), b).length; return length; // 5 } interface I { x: string } export function array_concat_interface() { let array1: I[] = [{ x: '' }, { x: '' }]; let obj = new B(); let i : I = obj; let length = array1.concat(i).length; return length; // 3 } export function array_concat_number_array() { let array: Array> = [[1, 2, 3], [4, 3, 5]]; let length = array.concat([2, 5, 2], [44, 2]).length; return length; // 4 } export function array_concat_string_array() { let array: Array> = [['hello', 'wasm'], ['world']]; console.log(array.concat(['123'])[0][0]); // hello return array.length; // 2 } ================================================ FILE: tests/samples/array_contain_closure.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ function outer() { let i = 10; function inner1() { i++; return i; } function inner2() { i--; return i; } return [inner1, inner2]; } export function containClosure() { const arr = outer(); const res = arr[0]; return res(); // 11 } ================================================ FILE: tests/samples/array_contain_func.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ /* eslint-disable @typescript-eslint/no-empty-function */ function inner1() { return 10; } function inner2() { return 20; } function outer() { return [inner1, inner2]; } export function containFunc() { const arr = outer(); const res = arr[1]; return res(); // 20 } ================================================ FILE: tests/samples/array_copyWithin.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A { x: string = 'xxx' constructor(x: string) { this.x = x; } } interface I { x: string } function output_number_arr(arr: number[]) { for (let i = 0; i < arr.length; ++i) { console.log(arr[i]); } } function output_string_arr(arr: string[]) { for (let i = 0; i < arr.length; ++i) { console.log(arr[i]); } } function output_boolean_arr(arr: boolean[]) { for (let i = 0; i < arr.length; ++i) { console.log(arr[i]); } } function output_interface_arr(arr: I[]) { for (let i = 0; i < arr.length; ++i) { console.log(arr[i].x); } } function output_class_arr(arr: A[]) { for (let i = 0; i < arr.length; ++i) { console.log(arr[i].x); } } export function array_copyWithin_number() { /* 0 < target < len */ let arr = [1, 2, 3, 4, 5, 6]; arr.copyWithin(0, 3, 5); //output_number_arr(arr); // 4 5 3 4 5 6 /* -len < target < 0 */ arr = [1, 2, 3, 4, 5, 6]; arr.copyWithin(-5, 3, 5); //output_number_arr(arr); // 1 4 5 4 5 6 /* target < -len */ arr = [1, 2, 3, 4, 5, 6]; arr.copyWithin(-20, 3, 5); //output_number_arr(arr); // 4 5 3 4 5 6 /* target >= len */ arr = [1, 2, 3, 4, 5, 6]; arr.copyWithin(6, 3, 5); //output_number_arr(arr); // 1 2 3 4 5 6 arr = [1, 2, 3, 4, 5, 6]; arr.copyWithin(20, 3, 5); //output_number_arr(arr); // 1 2 3 4 5 6 /*-len < start < 0*/ arr = [1, 2, 3, 4, 5, 6]; arr.copyWithin(0, -3, 5); //output_number_arr(arr); // 4 5 3 4 5 6 /* start < -len */ arr = [1, 2, 3, 4, 5, 6]; arr.copyWithin(1, -30, 5); //output_number_arr(arr); // 1 1 2 3 4 5 /* start >= len */ arr = [1, 2, 3, 4, 5, 6]; arr.copyWithin(1, 6, 5); //output_number_arr(arr); // 1 2 3 4 5 6 arr = [1, 2, 3, 4, 5, 6]; arr.copyWithin(1, 20, 5); //output_number_arr(arr); // 1 2 3 4 5 6 /* -len < end < 0 */ arr = [1, 2, 3, 4, 5, 6]; arr.copyWithin(0, 3, -1); //output_number_arr(arr); // 4 5 3 4 5 6 /* end < -len */ arr = [1, 2, 3, 4, 5, 6]; arr.copyWithin(0, 3, -10); //output_number_arr(arr); // 1 2 3 4 5 6 /* end >= len */ arr = [1, 2, 3, 4, 5, 6]; arr.copyWithin(0, 3, 10); //output_number_arr(arr); // 4 5 6 4 5 6 /* 0 < end <= start */ arr = [1, 2, 3, 4, 5, 6]; arr.copyWithin(0, 3, 2); //output_number_arr(arr); // 1 2 3 4 5 6 arr = [1, 2, 3, 4, 5, 6]; arr.copyWithin(0, 3, 3); //output_number_arr(arr); // 1 2 3 4 5 6 return arr.length; } export function array_copyWithin_string() { let arr = ["1", "2", "3", "4", "5"]; arr.copyWithin(0, 1, 3); output_string_arr(arr); // 2 3 3 4 5 } export function array_copyWithin_boolean() { let arr = [true, false, true]; arr.copyWithin(0, 1, 2); output_boolean_arr(arr); // false false true } export function array_copyWithin_class() { let arr: A[] = [new A("A1"), new A("A2"), new A("A3")]; arr.copyWithin(0, 1, 2); output_class_arr(arr); // A2 A2 A3 } export function array_copyWithin_interface() { let arr: I[] = [{ x: 'A1' }, { x: 'A2' }, { x: 'A3' }]; arr.copyWithin(0, 1, 2); output_interface_arr(arr); // A2 A2 A3 } ================================================ FILE: tests/samples/array_elem_get.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function arrayTest6() { const array1: number[] = [1, 2, 3]; const a = array1[0]; return a; } ================================================ FILE: tests/samples/array_elem_set.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function setElem() { const array1: number[] = [1, 2, 3]; array1[0] = 5; return array1[0]; } ================================================ FILE: tests/samples/array_every.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A { x: string = 'xxx' constructor(x: string) { this.x = x; } } interface I { x: string } export function array_every_string() { let words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present']; let result = words.every((word, idx, arr) => word.length > 6); console.log(result); // false words = ['exuberant', 'destruction', 'present']; result = words.every((word, idx, arr) => word.length > 6); console.log(result); // true } export function array_every_number() { let arr = [1, 2, 3, 4, 5, 6]; let result = arr.every((num, idx, arr) => num > 3); console.log(result); // false arr = [4, 5, 6]; result = arr.every((num, idx, arr) => num > 3); console.log(result); // true } export function array_every_boolean() { let arr = [true, false]; let result = arr.every((e, idx, arr) => e); console.log(result); // false arr = [true]; result = arr.every((e, idx, arr) => e); console.log(result); // true } export function array_every_class() { let arr: A[] = [new A("1"), new A("12"), new A("123"), new A("1234")]; let result = arr.every((obj, idx, arr) => obj.x.length > 2); console.log(result); // false arr = [new A("123"), new A("1234")]; result = arr.every((obj, idx, arr) => obj.x.length > 2); console.log(result); // true } export function array_every_interface() { let arr: I[] = [{ x: 'A1' }, { x: 'A2' }, { x: 'A3' }]; let result = arr.every((obj, idx, arr) => obj.x.length > 2); console.log(result); // false let arr2: I[] = [{ x: 'A11' }, { x: 'A22' }, { x: 'A33' }]; result = arr2.every((obj, idx, arr) => obj.x.length > 2); console.log(result); // true } ================================================ FILE: tests/samples/array_fill.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function array_fill_number() { let arr: Array = [123, 234, 456, 4, 453, 0, 456]; let ret = arr.fill(0, 2, 10); arr.forEach((val, idx, arr) => { console.log(idx, ":", val); }); ret.forEach((val, idx, arr) => { console.log(idx, ":", val); }); } export function array_fill_string() { let arr: Array = ["s123", "s234", "s456", "s4", "s453", "s0", "s456"]; let filled_arr: Array = arr.fill("hello", -10, 10); arr.forEach((val, idx, arr) => { console.log(idx, ":", val); }) filled_arr.forEach((val, idx, arr) => { console.log(idx, ":", val); }) } ================================================ FILE: tests/samples/array_filter.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function array_filter_number() { let arr: Array = [123, 234, 456, 4, 453, 0, 456]; let arr2 = arr.filter((val, idx, arr) => { return val > 200; }) return arr2.length; // 4 } export function array_filter_boolean() { let array1: boolean[] = [true, false, true, true]; let arr2 = array1.filter((val, idx, arr) => { return val; }) return arr2.length; // 3 } export function array_filter_string() { let array1: string[] = ['hello', 'world', 'hello', 'world']; let arr2 = array1.filter((val, idx, arr) => { return idx > 1; }) return arr2.length; // 2 } class A { x: string = 'xxx' } class B extends A { y: number = 1 } export function array_filter_class() { let b : A = new B(); b.x = 'hello'; let array1: A[] = [new A(), new A(), new A(), new A(), b]; let arr2 = array1.filter((val, idx, arr) => { return val.x === 'hello'; }) return arr2.length; // 1 } interface I { x: string } export function array_filter_interface() { let obj = new B(); let i : I = obj; let array1: I[] = [{ x: '' }, { x: '' }, i]; let arr2 = array1.filter((val, idx, arr) => { return val.x === ''; }) return arr2.length; // 2 } export function array_filter_number_array() { let array: Array> = [[1, 2, 3], [4, 3, 5], [2, 5, 2], [44, 2]]; let arr2 = array.filter((val, idx, arr) => { return val.length > 2; }) return arr2.length; // 3 } export function array_filter_string_array() { let array: Array> = [['h', 'sl'], ['world'], ['123']]; let arr2 = array.filter((val, idx, arr) => { return val[0] === 'h'; }) console.log(arr2[0][0]); return arr2.length; } export function push_in_callback() { let arr: Array = [123, 234, 456, 4, 453, 0, 456]; let arr2 = arr.filter((val, idx, arr) => { arr.push(300); return val > 200; }) return arr2.length; // 4 } export function modify_in_callback() { let arr: Array = [123, 234, 456, 4, 453, 0, 456]; let arr2 = arr.filter((val, idx, array) => { let index = idx + 1; if (index < array.length) { array[index] = 0; } return val > 200; }) return arr2.length; // 0 } export function nested_filter() { let array: Array> = [[1, 2, 3], [4, 3, 5], [2, 5, 2], [44, 2]]; let arr2 = array.filter((val, idx, arr) => { let a = val.filter((val, idx, arr) => { return val > 5; }) return a.length > 0; }) return arr2.length; // 1 } export function callback_use_closure_var() { let threshold = 200; let arr: Array = [123, 234, 456, 4, 453, 0, 456]; let arr2 = arr.filter((val, idx, arr) => { return val > threshold; }) return arr2.length; // 4 } ================================================ FILE: tests/samples/array_find.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function array_find_number() { let array: number[] = [100, 200, 300, 400]; let element1 = array.find((val, idx, arr) => { return val > 200; }) console.log(element1); // 300 let element2 = array.find((val, idx, arr) => { return val > 500; }) console.log(element2); // undefined } export function array_find_boolean() { let array: boolean[] = [true, false, true, true]; let element = array.find((val, idx, arr) => { return val; }) console.log(element); // true } export function array_find_string() { let array: string[] = ['hello', 'wasm', 'hello', 'awesome']; let element = array.find((val, idx, arr) => { return idx > 1; }) console.log(element); // hello } export function array_find_obj() { let array = [{a: 1, b: false}, {a: 2, b: false}, {a: 3, b: true}]; let element = array.find((val, idx, arr) => { return val.a == 3; })!; console.log(element.b); // true } ================================================ FILE: tests/samples/array_findIndex.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function array_findIndex_number() { const arr = [1, 2, 3, 4, 5]; const foundIndex = arr.findIndex((element, idx, arr) => { return element > 2; }); const notfoundIndex = arr.findIndex((element, idx, arr) => { return element > 6; }); console.log('foundIndex:', foundIndex); console.log('notfoundIndex:', notfoundIndex); } export function array_findIndex_string() { const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present']; const result = words.findIndex((word, idx, arr) => word.length > 6); const noresult = words.findIndex((word, idx, arr) => word.length > 20); console.log('result:', result); console.log('noresult:', noresult); } export function array_findIndex_boolean() { const boolArr = [false, true, false, true]; const index = boolArr.findIndex((element, idx, arr) => !!element); console.log(index); } export function array_findIndex_class() { const array = [ { name: "Alice", age: 25 }, { name: "Bob", age: 30 }, { name: "Charlie", age: 35 }, { name: "David", age: 40 } ]; const index1 = array.findIndex((person, idx, arr) => person.age === 30); console.log(index1); const index2 = array.findIndex((person, idx, arr) => person.age === 50); console.log(index2); } export function array_findIndex_interface() { interface SomeInterface { id: number; name: string; } let someArray: SomeInterface[] = [ { id: 1, name: "John" }, { id: 2, name: "Mary" } ]; let index = someArray.findIndex((item, idx, arr) => item.id === 2); console.log(index); } ================================================ FILE: tests/samples/array_foreach.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function array_foreach_number() { let arr: Array = [123, 234, 456, 4, 453, 0, 456]; arr.forEach((val, idx, arr) => { console.log(idx, ":", val); }); // console.log(ret); // undefine } export function array_foreach_string() { let arr: Array = ["s123", "s234", "s456", "s4", "s453", "s0", "s456"]; arr.forEach((val, idx, arr) => { console.log(idx, ":", val); }); // console.log(ret); // undefine } export function array_foreach_closure() { let map: any = new Map() let arr = new Array() for(let i=0;i<1;i++) { arr.push(i) } arr.forEach((item)=>{ map.set(item, item) }) return map.size as number } ================================================ FILE: tests/samples/array_includes.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A { x: string = 'xxx' constructor(x: string) { this.x = x; } } interface I { x: string } export function array_includes_number() { let arr = [1, 2, 3, 4, 5]; console.log(arr.includes(2, 10)); // false console.log(arr.includes(2, 2)); // false console.log(arr.includes(2, 3)); // false console.log(arr.includes(10, undefined)); // false console.log(arr.includes(10, 3.1)); // false console.log(arr.includes(2, -10)); // true console.log(arr.includes(2, 1)); // true console.log(arr.includes(2, undefined)); // true console.log(arr.includes(5, 3.1)); // true } export function array_includes_string() { let arr = ["1", "2", "3", "4", "5", "6"]; console.log(arr.includes("21", 0)); // false console.log(arr.includes("1", 2.1)); // false console.log(arr.includes("1", undefined)); // true console.log(arr.includes("1", 0)); // true let arr2 = ["a", "b", "c"]; console.log(arr2.includes("a", -2)); // false } export function array_includes_boolean() { let arr = [true, false]; console.log(arr.includes(false, 20)); // false; console.log(arr.includes(false, undefined)); // true; console.log(arr.includes(false, 0.5)); // true; } export function array_includes_class() { let A1 = new A("1"); let A2 = new A("2"); let A3 = new A("3"); let A4 = new A("4"); let A5 = new A("5"); let arr: A[] = [A1, A2, A3, A4]; console.log(arr.includes(A1, 20)); // false console.log(arr.includes(A5, 0)); // false console.log(arr.includes(A1, 0.5)); // true console.log(arr.includes(A1, undefined)); // true } export function array_includes_interface() { let A1: I = { x: 'A1' }; let A2: I = { x: 'A2' }; let A3: I = { x: 'A3' }; let arr: I[] = [A1, A2]; console.log(arr.includes(A1, 20)); // false console.log(arr.includes(A3, 0)); // false console.log(arr.includes(A1, 0.5)); // true console.log(arr.includes(A1, undefined)); // true } ================================================ FILE: tests/samples/array_indexOf.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function array_indexOf_number() { let array: number[] = [1, 2, 3, 4, 5, 6]; let index: number = array.indexOf(4, 0); return index; // 3 } export function array_indexOf_boolean() { let array: boolean[] = [true, false]; let index: number = array.indexOf(false, 0); return index; // 1 } export function array_indexOf_string() { let array1: string[] = ['hello', 'world', "hello", "wasm"]; let index: number = array1.indexOf("wasm", 1); return index; // 3 } class A { a: string = 'hello' } export function array_indexOf_class() { let a1 = new A(); let a2 = new A(); let array1: A[] = [a1, a2]; let index: number = array1.indexOf(a2, 0); return index; // 1 } ================================================ FILE: tests/samples/array_join.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function array_join_string() { let array: string[] = ['hello', 'wasm', 'hello', 'world']; let s1 = array.join('$'); console.log(s1); // hello$wasm$hello$world let s1_len = s1.length; console.log(s1_len); // 22 let s2 = array.join(); console.log(s2); // hello,wasm,hello,world let s2_len = s2.length; console.log(s2_len); // 22 } ================================================ FILE: tests/samples/array_lastIndexOf.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function array_lastIndexOf_number() { let array: number[] = [1, 2, 3, 4, 5, 6]; let lastindex: number = array.lastIndexOf(4, 0); return lastindex; // 3 } export function array_lastIndexOf_boolean() { let array: boolean[] = [true, false]; let lastindex: number = array.lastIndexOf(false, 0); return lastindex; // 1 } export function array_lastIndexOf_string() { let array: string[] = ['wasm', 'hello', 'world', "wasm", "array"]; let lastindex: number = array.lastIndexOf("wasm", -2); return lastindex; // 3 } class A { a: string = 'hello' } export function array_lastIndexOf_class() { let a1 = new A(); let a2 = new A(); let a3 = new A(); let array1: A[] = [a1, a2, a3]; let index: number = array1.lastIndexOf(a3, 0); return index; // 2 } ================================================ FILE: tests/samples/array_map.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function array_map_number() { let arr: Array = [123, 234, 456, 4, 453, 0, 456]; let arr2 = arr.map((val, idx, arr) => { return val + 100; }) return arr2[0]; // 223 } export function array_map_boolean() { let array1: boolean[] = [true, false, true, true]; let arr2 = array1.map((val, idx, arr) => { return [val, val, !val] }) return arr2[1].length; // 3 } export function array_map_string() { let array1: string[] = ['hello', 'world', 'hello', 'world']; let arr2 = array1.map((val, idx, arr) => { return val + '!'; }) console.log(arr2[0]); return arr2[0].length; // 6 } class A { x: string = 'xxx' } class B extends A { y: number = 1 } export function array_map_class() { let b : A = new B(); b.x = 'hello'; let array1: A[] = [new A(), new A(), new A(), new A(), b]; let arr2 = array1.map((val, idx, arr) => { return val.x + '!'; }) console.log(arr2[1]); // xxx! console.log(arr2[4]); // hello! return arr2[4].length; // 6 } interface I { x: string } export function array_map_interface() { let obj = new B(); let i : I = obj; let array1: I[] = [{ x: '' }, { x: '' }, i]; let arr2 = array1.map((val, idx, arr) => { return { x: val.x + '!', y: val.x + '!!', z: idx }; }) console.log(arr2[0].x); // ! console.log(arr2[2].y); // xxx!! return arr2.length; // 3 } export function array_map_number_array() { let array: Array> = [[1, 2, 3], [4, 3, 5], [2, 5, 2], [44, 2]]; let arr2 = array.map((val, idx, arr) => { val.pop(); return val; }) return arr2[2].length; // 2 } export function array_map_string_array() { let array: Array> = [['h', 'sl'], ['world'], ['123']]; let arr2 = array.map((val, idx, arr) => { return val[0] + '^'; }) console.log(arr2[1]); // world^ return arr2.length; // 3 } export function array_map_func() { let arr: Array = [123, 234, 456, 4, 453, 0, 456]; let arr2 = arr.map((val, idx, arr) => { return (x: number) => { return x + val }; }) return arr2[4](1); // 454 } ================================================ FILE: tests/samples/array_nested_array.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function nestedArray() { const array1: Array = new Array(1); array1[0] = [100]; return array1[0][0]; } ================================================ FILE: tests/samples/array_nested_literal.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function nestedLiteral() { const array1 = [ [1, 2], [3, 4], ]; return array1[1][0]; } ================================================ FILE: tests/samples/array_nested_literal_array.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function nestedLiteralArrayInOneLayer() { const array1 = [new Array('hi')]; array1[0][0] = 'hello'; return array1; } export function nestedLiteralArrayInMulLayer() { const array1 = [new Array>(new Array('hi'))]; array1[0][0][0] = 'hello'; return array1; } ================================================ FILE: tests/samples/array_new_array.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function implElemType() { const a: number[] = new Array(3); a[1] = 10; const b = new Array(1, 2); return a[1] + b[1]; } ================================================ FILE: tests/samples/array_new_array_number.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function newArrayNumber() { const array1 = new Array(1); array1[0] = 3; return array1[0]; } export function newArrayNumberWithParam(n: number) { const array1 = new Array>(n); array1[0] = new Array(n); array1[0][0] = n; return array1[0][0]; } ================================================ FILE: tests/samples/array_new_array_string.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function newArrayString() { const array1 = new Array('hello', 'world'); array1[0] = 'hi'; return array1; } ================================================ FILE: tests/samples/array_new_literal_any.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function newLiteralExplicitAny() { const array1: any[] = [1, 'hi', true, { a: 1 }]; return array1[0] as number; } export function newLiteralNonExplicitAny() { const array1 = [1, 'hi', true, { a: 1 }]; return array1[0] as number; } ================================================ FILE: tests/samples/array_new_literal_boolean.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function newLiteralBoolean() { let array1: boolean[] = [true, false, true]; return array1[0]; } ================================================ FILE: tests/samples/array_new_literal_number.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function newLiteralNumberWithLiteralType() { const array1: number[] = [1, 2, 3]; return array1[2]; } export function newLiteralNumberWithArrayType() { const array1: Array = [1, 2, 3]; return array1[2]; } export function newLiteralNumberWithoutInit() { const array1: Array = []; return array1; } ================================================ FILE: tests/samples/array_new_literal_string.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function newLiteralString() { let array1: string[] = ['hello', 'world']; return array1; } ================================================ FILE: tests/samples/array_pop.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function array_pop_number() { let array1: number[] = [1, 2, 4]; let x = array1.pop(); return array1.length; // 2 } export function array_pop_boolean() { let array1: boolean[] = [true, false, false]; let b = array1.pop(); return array1.length; // 2 } export function array_pop_string() { let array1: string[] = ['hello', 'world']; let s = array1.pop(); return array1.length; // 1 } class A { x: string = 'xxx' } class B extends A { y: number = 1 } export function array_pop_class() { let array1: A[] = [new A(), new A()]; let o = array1.pop(); return array1.length; // 1 } interface I { x: string } export function array_pop_interface() { let array1: I[] = [{ x: '' }, { x: '' }]; let i = array1.pop(); return array1.length; // 1 } export function array_pop_number_array() { let array: Array> = [[1, 2, 3], [4, 3, 5], [2, 5, 2], [44, 2]]; let a = array.pop(); return array.length + a.length; // 5 (3 + 2) } export function array_pop_string_array() { let array: Array> = [['h', 'sl'], ['world'], ['123']]; console.log(array.pop()[0]); return array.length; // 2 } ================================================ FILE: tests/samples/array_push.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function array_push_number() { let array1: number[] = [1, 2]; let length: number = array1.push(3, 4); return length; } export function array_push_number_with_empty() { const a: number[] = []; a.push(10); return a[0]; } class ParamsObject { key = '123'; val = ''; } class RouteInfo { params: Array = []; } export function array_class2() { let route: RouteInfo = new RouteInfo(); route.params.push(new ParamsObject()); console.log(route.params[0].key); route.params = new Array(); route.params.push(new ParamsObject()); console.log(route.params[0].key); } export function array_push_boolean() { let array1: boolean[] = [true, false]; let length: number = array1.push(true, false); return length; } export function array_push_string() { let array1: string[] = ['hello', 'world']; let length: number = array1.push('hello', 'world'); return length; } class A { x: string = 'xxx' } class B extends A { y: number = 1 } export function array_push_class() { let array1: A[] = [new A(), new A()]; let b : A = new B(); let length: number = array1.push(new A(), new A(), b); return length; } interface I { x: string } export function array_push_interface() { let array1: I[] = [{ x: '' }, { x: '' }]; let obj = new B(); let i : I = obj; let length = array1.push(i); return length; } export function array_push_number_array() { let array: Array> = [[1, 2, 3], [4, 3, 5]]; let length = array.push([2, 5, 2], [44, 2]); return length; } export function array_push_string_array() { let array: Array> = [['h', 'sl'], ['world']]; let length = array.push(['123']); console.log(array[2][0]); return length; } ================================================ FILE: tests/samples/array_reduce.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A { x: string = 'xxx' constructor(x: string) { this.x = x; } } interface I { x: string } export function array_reduce_test() { let arr = [1, 2, 3, 4, 5, 6]; console.log(arr.reduce((accumulator, currentValue) => { return accumulator + currentValue; }, 0)); // 21 } export function array_reduce_number() { let arr = [1, 2, 3, 4, 5, 6]; console.log(arr.reduce((accumulator, currentValue) => { return accumulator + currentValue; }, 0)); // 21 } export function array_reduce_string() { let arr = ["1", "2", "3", "4"]; console.log(arr.reduce((accumulator, currentValue) => { return accumulator + currentValue; }, "0")); // 01234 } export function array_reduce_boolean() { let arr = [true, false]; console.log(arr.reduce((accumulator, currentValue) => { return currentValue; }, true)); // false } export function array_reduce_class() { let A1 = new A("1"); let A2 = new A("2"); let A3 = new A("3"); let A4 = new A("4"); let A5 = new A("5"); let arr: A[] = [A1, A2, A3, A4, A5]; console.log(arr.reduce((accumulator, currentValue) => { return new A(accumulator.x + (currentValue.x)); }, A3).x); // 312345 } export function array_reduce_interface() { let A1: I = { x: 'A1' }; let A2: I = { x: 'A2' }; let A3: I = { x: 'A3' }; let arr: I[] = [A1, A2]; console.log(arr.reduce((accumulator, currentValue) => { return currentValue; }, A3).x); // A2 } ================================================ FILE: tests/samples/array_reduceRight.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A { x: string = 'xxx' constructor(x: string) { this.x = x; } } interface I { x: string } export function array_reduceRight_number() { let arr = [1, 2, 3, 4, 5, 6]; console.log(arr.reduceRight((accumulator, currentValue) => { return accumulator + currentValue; }, 0)); // 21 } export function array_reduceRight_string() { let arr = ["1", "2", "3", "4"]; console.log(arr.reduceRight((accumulator, currentValue) => { return accumulator + (currentValue); }, "0")); // 04321 } export function array_reduceRight_boolean() { let arr = [true, false]; console.log(arr.reduceRight((accumulator, currentValue) => { return currentValue; }, true)); // true } export function array_reduceRight_class() { let A1 = new A("1"); let A2 = new A("2"); let A3 = new A("3"); let A4 = new A("4"); let A5 = new A("5"); let arr: A[] = [A1, A2, A3, A4, A5]; console.log(arr.reduceRight((accumulator, currentValue) => { return new A(accumulator.x + (currentValue.x)); }, A3).x); // 354321 } export function array_reduceRight_interface() { let A1: I = { x: 'A1' }; let A2: I = { x: 'A2' }; let A3: I = { x: 'A3' }; let arr: I[] = [A1, A2]; console.log(arr.reduceRight((accumulator, currentValue) => { return currentValue; }, A3).x); // A1 } ================================================ FILE: tests/samples/array_reverse.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function array_reverse_number() { let array: number[] = [1, 2, 4]; let x = array.reverse(); return array.length; // 3 } export function array_reverse_boolean() { let array: boolean[] = [true, false, false]; let b = array.reverse(); return array.length; // 3 } export function array_reverse_string() { let array: string[] = ['hello', 'world']; let s = array.reverse(); return array.length; // 2 } class A { x: string = 'xxx' } class B extends A { y: number = 1 } export function array_reverse_class() { let array: A[] = [new A(), new A()]; let o = array.reverse(); return array.length; // 2 } interface I { x: string } export function array_reverse_interface() { let array: I[] = [{ x: '' }, { x: '' }]; let i = array.reverse(); return array.length; // 2 } export function array_reverse_number_array() { let array: Array> = [[1, 2, 3], [4, 3, 5], [2, 5, 2], [44, 2]]; let a = array.reverse(); console.log(a[0][0]); // 44 return array.length + a.length; // 8 (4 + 4) } export function array_reverse_string_array() { let array: Array> = [['h', 'sl'], ['world'], ['123']]; console.log(array.reverse()[0][0]); // 123 return array.length; // 3 } ================================================ FILE: tests/samples/array_shift.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function array_shift_number() { let array: number[] = [1, 2, 4]; let x = array.shift(); return array.length; // 2 } export function array_shift_boolean() { let array: boolean[] = [true, false, false]; let b = array.shift(); return array.length; // 2 } export function array_shift_string() { let array: string[] = ['hello', 'world']; let s = array.shift(); return array.length; // 1 } class A { x: string = 'xxx' } class B extends A { y: number = 1 } export function array_shift_class() { let array: A[] = [new A(), new A()]; let o = array.shift(); return array.length; // 1 } interface I { x: string } export function array_shift_interface() { let array: I[] = [{ x: '' }, { x: '' }]; let i = array.shift(); return array.length; // 1 } export function array_shift_number_array() { let array: Array> = [[1, 2, 3], [4, 3, 5], [2, 5, 2], [44, 2]]; let a = array.shift(); // [1, 2, 3] console.log(a[0]); // 1 return array.length + a.length; // 6 (3 + 3) } export function array_shift_string_array() { let array: Array> = [['h', 'sl'], ['world'], ['123']]; console.log(array.shift()[0]); // h return array.length; // 2 } ================================================ FILE: tests/samples/array_slice.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function array_slice() { let arr: Array = [123, 234, 456, 4, 453, 0, 456]; let ret = arr.slice(5, 10); // [0, 456] ret.forEach((val, idx, arr) => { console.log(idx, ":", val); }); } export function array_slice_empty() { let arr: Array = [123, 234, 456, 4, 453, 0, 456]; let ret = arr.slice(5, 5); // empty ret.forEach((val, idx, arr) => { console.log(idx, ":", val); }); } export function array_slice_endIdxisUndefined() { let arr: Array = ['a', 'b', 'c'] let ret = arr.slice(0); ret.forEach((val, idx, arr) => { console.log(idx, ":", val); }); } export function array_set_length() { let arr: Array = ['a', 'b', 'c'] arr.length = 0; arr.push('d','e','f','g') for(const a of arr) console.log(a) } ================================================ FILE: tests/samples/array_some.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A { x: string = 'xxx' constructor(x: string) { this.x = x; } } interface I { x: string } export function array_some_string() { let words = ['spray', 'limit', 'elite']; let result = words.some((word, idx, arr) => word.length > 6); console.log(result); // false words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present']; result = words.some((word, idx, arr) => word.length > 6); console.log(result); // true } export function array_some_number() { let arr = [1, 2, 3]; let result = arr.some((num, idx, arr) => num > 3); console.log(result); // false arr = [1, 2, 3, 4, 5, 6]; result = arr.some((num, idx, arr) => num > 3); console.log(result); // true } export function array_some_boolean() { let arr = [false]; let result = arr.some((e, idx, arr) => e); console.log(result); // false arr = [true, false]; result = arr.some((e, idx, arr) => e); console.log(result); // true } export function array_some_class() { let arr: A[] = [new A("1"), new A("12")]; let result = arr.some((obj, idx, arr) => obj.x.length > 2); console.log(result); // false arr = [new A("1"), new A("12"), new A("123"), new A("1234")]; result = arr.some((obj, idx, arr) => obj.x.length > 2); console.log(result); // true } export function array_some_interface() { let arr: I[] = [{ x: 'A1' }, { x: 'A2' }, { x: 'A3' }]; let result = arr.some((obj, idx, arr) => obj.x.length > 2); console.log(result); // false let arr2: I[] = [{ x: 'A1' }, { x: 'A2' }, { x: 'A33' }]; result = arr2.some((obj, idx, arr) => obj.x.length > 2); console.log(result); // true } ================================================ FILE: tests/samples/array_sort.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function array_sort_number() { let arr: Array = [12, 23, 6, 4, 45, 0, 56]; let ret = arr.sort((a, b) => a - b); arr.forEach((val, idx, arr) => { console.log(idx, ":", val); }); ret.forEach((val, idx, arr) => { console.log(idx, ":", val); }); } ================================================ FILE: tests/samples/array_specialization.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A { func1(...data: any[]): number { return 100; } } export function test() { const a: A = new A(); const ret = a.func1(1, 2); console.log(ret); } ================================================ FILE: tests/samples/array_splice.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ function output_number_arr(arr: number[]) { for (let i = 0; i < arr.length; ++i) { console.log(arr[i]); } } function output_arr_boolean(arr: boolean[]) { for (let i = 0; i < arr.length; ++i) { console.log(arr[i]); } } function output_arr_string(arr: string[]) { for (let i = 0; i < arr.length; ++i) { console.log(arr[i]); } } class A { x: string = 'xxx' constructor(x: string) { this.x = x; } } function output_arr_class(arr: A[]) { for (let i = 0; i < arr.length; ++i) { console.log(arr[i].x); } } interface I { x: string } function output_arr_interface(arr: I[]) { for (let i = 0; i < arr.length; ++i) { console.log(arr[i].x); } } export function array_splice_number() { /** the value of start:[0,len-1] * the value of deleteCount > 0 */ let arr: Array = [1.5, 2.5, 3, 4, 5, 6.5, 7.5, 8, 9, 10]; let removed = arr.splice(5.5, 2.5, 1.5, 2.5, 3.5); // decimal //console.log("case1"); //output_number_arr(arr); // 1.5 2.5 3 4 5 1.5 2.5 3.5 8 9 10 //output_number_arr(removed); // 6.5 7.5 arr = [1.5, 2.5, 3, 4, 5, 6.5, 7.5, 8, 9, 10]; removed = arr.splice(5.5, 2.5); //console.log("case2"); //output_number_arr(arr); // 1.5 2.5 3 4 5 8 9 10 //output_number_arr(removed); // 6.5 7.5 arr = [1.5, 2.5, 3, 4, 5, 6.5, 7.5, 8, 9, 10]; removed = arr.splice(5.5, 7.5, 1.5, 2.5); // start + delete_count > len //console.log("case3"); //output_number_arr(arr); // 1.5 2.5 3 4 5 1.5 2.5 //output_number_arr(removed); // 6.5 7.5 8 9 10 /** the value of start:[-len + 1,0), start will be converted to start + len * the value of deleteCount:[0,len] */ arr = [1.5, 2.5, 3, 4, 5, 6.5, 7.5, 8, 9, 10]; removed = arr.splice(-5.5, 2.5, 1.5); // delete count > count of new elements //console.log("case4"); //output_number_arr(arr); // 1.5 2.5 3 4 5 1.5 8 9 10 //output_number_arr(removed); // 6.5 7.5 arr = [1.5, 2.5, 3, 4, 5, 6.5, 7.5, 8, 9, 10]; removed = arr.splice(-5.5, 2.5); //console.log("case5"); //output_number_arr(arr); // 1.5 2.5 3 4 5 8 9 10 //output_number_arr(removed); // 6.5 7.5 /** the value of start >= len, no elements will be removed * the new elements will be added at the end of array */ arr = [1.5, 2.5, 3, 4, 5, 6.5, 7.5, 8, 9, 10]; removed = arr.splice(11, 2.5, 1.5, 2.5); //console.log("case6"); //output_number_arr(arr); // 1.5 2.5 3 4 5 6.5 7.5 8 9 10 1.5 2.5 //output_number_arr(removed); arr = [1.5, 2.5, 3, 4, 5, 6.5, 7.5, 8, 9, 10]; removed = arr.splice(10, 0); //console.log("case7"); //output_number_arr(arr); // 1.5 2.5 3 4 5 6.5 7.5 8 9 10 //output_number_arr(removed); /** the value of start:[0,len -1] * the value of deleteCount <= 0, no elements will be removed * the new elements will be added behind arr[start] */ arr = [1.5, 2.5, 3, 4, 5, 6.5, 7.5, 8, 9, 10]; removed = arr.splice(5, 0, 11); //console.log("case8"); //output_number_arr(arr); // 1.5 2.5 3 4 5 11 6.5 7.5 8 9 10 //output_number_arr(removed); arr = [1.5, 2.5, 3, 4, 5, 6.5, 7.5, 8, 9, 10]; removed = arr.splice(5, -10, 11); //console.log("case9"); //output_number_arr(arr); // 1.5 2.5 3 4 5 11 6.5 7.5 8 9 10 //output_number_arr(removed); /** the value of start < -len, start will be converted to 0 * the value of deleteCount:[0, len -1] * */ arr = [1.5, 2.5, 3, 4, 5, 6.5, 7.5, 8, 9, 10]; removed = arr.splice(-20, 2, 11); //console.log("case10"); //output_number_arr(arr); // 11 3 4 5 6.5 7.5 8 9 10 //output_number_arr(removed); // 1.5 2.5 arr.splice(0); console.log(arr.length); return removed.length; } export function array_splice_boolean() { let arr: boolean[] = [true, false, true]; let removed = arr.splice(1, 1, true); //output_arr_boolean(arr); // true true true //output_arr_boolean(removed); // false return removed.length; } export function array_splice_string() { let arr: string[] = ["hello", "world", "nihao"]; let removed = arr.splice(1, 1, "hi"); //output_arr_string(arr); // hello hi nihao //output_arr_string(removed); // world return removed.length; } export function array_splice_class() { let arr: A[] = [new A("A1"), new A("A2"), new A("A3")]; let removed = arr.splice(1, 1, new A("A4")); //output_arr_class(arr); // A1 A4 A3 //output_arr_class(removed); // A2 return removed.length; } export function array_splice_interface() { let arr: I[] = [{ x: 'A1' }, { x: 'A2' }, { x: 'A3' }]; let obj = new A("A4"); let i: I = obj; let removed = arr.splice(1, 1, i); //output_arr_interface(arr); // A1 A4 A3 //output_arr_interface(removed); // A2 return removed.length; } ================================================ FILE: tests/samples/array_unshift.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function array_unshift_number() { let array: number[] = [3, 4]; let length: number = array.unshift(1, 2); return length; // 4 } export function array_unshift_boolean() { let array: boolean[] = [true, false]; let length: number = array.unshift(true, false); return length; // 4 } export function array_unshift_string() { let array: string[] = ['hello', 'world']; let length: number = array.unshift('hi', 'level'); return length; // 4 } class A { x: string = 'xxx' } class B extends A { y: number = 1 } export function array_unshift_class() { let array: A[] = [new A(), new A()]; let b : A = new B(); let length: number = array.unshift(new A(), new A(), b); return length; // 5 } interface I { x: string } export function array_unshift_interface() { let array: I[] = [{ x: '' }, { x: '' }]; let obj = new B(); let i : I = obj; let length = array.unshift(i); return length; // 3 } export function array_unshift_number_array() { let array: Array> = [[3, 4, 5], [4, 5, 6]]; let length = array.unshift([0, 1, 2], [1, 2, 3]); return length; // 4 } export function array_unshift_string_array() { let array: Array> = [['wasm', 'hello'], ['world']]; let length = array.unshift(['hi']); console.log(array[1][0]); // wasm return length; // 3 } ================================================ FILE: tests/samples/array_wasm_basic_type.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function arrayLength() { const a1: i32 = 10; const arr1_i32: i32[] = new Array(a1); const arr1_default = new Array(a1); // default is any type arr1_i32[0] = 32; arr1_default[0] = 30; console.log(arr1_i32[0]); console.log(arr1_default[0]); const a2: i64 = 20; const arr2_i64: i64[] = new Array(a2); const arr2_default = new Array(a2); // default is any type arr2_i64[0] = 64; arr2_default[0] = 60; console.log(arr2_i64[0]); console.log(arr2_default[0]); const a3: f32 = 30; const arr3_f32: f32[] = new Array(a3); const arr3_default = new Array(a3); // default is any type arr3_f32[0] = 32.00; arr3_default[0] = 30.30; console.log(arr3_f32[0]); console.log(arr3_default[0]); const a4: f64 = 40; const arr4_f64: f64[] = new Array(a4); const arr4_default = new Array(a4); // default is any type arr4_f64[0] = 64.64; arr4_default[0] = 60.60; console.log(arr4_f64[0]); console.log(arr4_default[0]); } ================================================ FILE: tests/samples/arraybuffer_basic.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function getArrayBufferLength() { const a = new ArrayBuffer(10); const a_sub = a.slice(0, 1); console.log(a.byteLength); console.log(a_sub.byteLength); } export function arrayBufferIsView() { const a = new ArrayBuffer(10); const d = new DataView(a, 1, 5); console.log(ArrayBuffer.isView(a)); console.log(ArrayBuffer.isView(d)); } ================================================ FILE: tests/samples/assign_different_type.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A{ x: string[]; constructor(){ this.x = []; } } export function assignStringArray() { const a = new A(); a.x.push('hello'); console.log(a.x[0]); } interface I { instance: number; } type cbType = (v: I) => number; class B { x: Array; constructor() { this.x = []; } } function elemFunc(a: I) { return a.instance; } export function assignClosureArray() { const a = new B(); const i: I = { instance: 100, }; a.x.push(elemFunc); console.log(a.x[0](i)); } ================================================ FILE: tests/samples/auto_box_unbox.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A { x: number; constructor(xx: number) { this.x = xx; } } export function autoBoxUnboxAssign() { let a: any = 1; let b: number = a; console.log(b); // 1 b = 2; a = b; console.log(a); // 2 a = 'str'; let b1: string = a; console.log(b1); // str a = 'str1'; b1 = a; console.log(b1); // str1 a = new A(10); let b2: A = a; console.log(b2.x); // 10 a = new A(11); b2 = a; console.log(b2.x); // 11 } function helper(a: any) { const b: A = a; return b.x; } function helper1(a: A) { return a.x; } function helper2(): any { const a = new A(10); return a; } function helper3(): A { const a = new A(10); return a; } export function autoBoxUnboxObjParam() { const a: any = new A(20); console.log(helper(new A(10)) + helper1(a)); // 30 console.log((helper2() as A).x + helper3().x); // 20 } class B { x: any; y: any = new A(10); z1: A = new A(-1); static z2: any = new A(10); constructor(xx: A) { this.x = xx; } add() { return (this.x as A).x + (this.y as A).x + (B.z2 as A).x; } box(a: A) { this.x = a; } unbox(a: any) { this.z1 = a; } } export function autoBoxunboxObjField() { const b = new B(new A(10)); console.log(b.add()); // 30 const a1 = new A(100); b.box(a1); console.log((b.x as A).x); // 100; b.unbox(b.x); console.log(b.z1.x); // 100; } ================================================ FILE: tests/samples/base_function_call.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class Base { static serialNumber = 0; constructor() { console.log("constructor from Base"); } static say() { console.log(Base.serialNumber); console.log("Base"); } } class A extends Base { static serialNumber = 1; x: number; constructor(x: number) { super(); this.x = x; console.log("constructor from A"); } log() { console.log('x: ', this.x); } static say() { console.log(A.serialNumber); super.say(); } } class B extends A { static serialNumber = 2; y: string; constructor(x: number, y: string) { super(x); this.y = y; console.log("constructor from B"); } log() { console.log('y: ', this.y); super.log(); } static say() { console.log(B.serialNumber); super.say(); } } export function test() { let b: B = new B(1, "hello"); b.log(); B.say(); } ================================================ FILE: tests/samples/block_closure.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ function reBindingBlockClosure() { let arr: Array<() => number> = []; for (let i = 0; i < 10; i++) { arr.push(() => i); } return arr; } function reBindingBlockClosures() { let arr: Array<() => number> = []; for (let i = 0, j = 0; i < 10 && j < 10; i++, j++) { arr.push(() => i + j); } return arr; } function notReBindingBlockClosure() { let arr: Array<() => number> = []; for (var i = 0; i < 10; i++) { arr.push(() => i); } return arr; } function partBindingBlockClosures() { let arr: Array<() => number> = []; let j = 0; for (let i = 0; i < 10; i++) { arr.push(() => i + j); } return arr; } function partBindingBlockClosuresWithChangeJ() { let arr: Array<() => number> = []; let j = 0; for (let i = 0; i < 10 && j < 10; i++, j++) { arr.push(() => i + j); } return arr; } function reBindingBlockClosureWithVarDefined() { let arr: Array<() => number> = []; let j = 0; for (let i = 0; i < 10 && j < 10; i++, j++) { let a = i; let b = j; arr.push(() => a + b); } return arr; } function blockClosureWithWhile() { let arr: Array<() => number> = []; let i = 0; while(i < 10) { arr.push(() => i); i++; } return arr; } function blockClosureWithWhileWithVarDefined() { let arr: Array<() => number> = []; let i = 0; while(i < 10) { let a = i; arr.push(() => a); i++; } return arr; } export function getRes() { let arr = reBindingBlockClosure(); console.log(arr[1]()); console.log(arr[5]()); arr = reBindingBlockClosures(); console.log(arr[1]()); console.log(arr[5]()); arr = notReBindingBlockClosure(); console.log(arr[1]()); console.log(arr[5]()); arr = partBindingBlockClosures(); console.log(arr[1]()); console.log(arr[5]()); arr = partBindingBlockClosuresWithChangeJ(); console.log(arr[1]()); console.log(arr[5]()); arr = reBindingBlockClosureWithVarDefined(); console.log(arr[1]()); console.log(arr[5]()); arr = blockClosureWithWhile(); console.log(arr[1]()); console.log(arr[5]()); arr = blockClosureWithWhileWithVarDefined(); console.log(arr[1]()); console.log(arr[5]()); } ================================================ FILE: tests/samples/block_inner_block.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function innerBlock() { let a: number; let b = 1; { const c = 9; a = 2; b = c; } return a + b; } ================================================ FILE: tests/samples/boolean_basic.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function booleanBasicTrue(): boolean { const i1 = true; return i1; } export function booleanBasicFalse(): boolean { const i1 = false; return i1; } export function booleanCmp() { const f1 = false; console.log(f1 !== false); // false console.log(f1 === false); // true } ================================================ FILE: tests/samples/boolean_logic_operator.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function logicOr(): boolean { const j5 = 123, j6 = 456; const i7 = j5 > 200 || j6 > 200; return i7; } export function logicAnd(): boolean { const j7 = 123, j8 = 456; const i8 = j7 > 200 && j8 < 200; return i8; } export function conditionExpr(): number { const j11 = 123, j12 = 456; let i10 = j11 > j12 ? 1 : 2; // not support const type, for its union type. return i10; } ================================================ FILE: tests/samples/boolean_not.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function notNumber(): boolean { const j1 = 123; const i4 = !j1; return i4; } export function notBoolean(): boolean { const j2 = false; const i5 = !j2; return i5; } export function notWithLogicOperator(): number { let i = 10, j = 10; let k = !(!i && !j); return 0; } ================================================ FILE: tests/samples/builtin_array.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function length() { const a = [1, 2, 3]; const b = [4]; const aLen = a.length; return aLen; } export function isArray_anyType() { const a: any = [1, 2, 3]; const b = Array.isArray(a); return b; } export function isArray_arrayType() { const a = [1, 2, 3]; const b = Array.isArray(a); return b; } ================================================ FILE: tests/samples/builtin_console.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function consoleLog() { let obj: any = { a: 1, b: 2, }; console.log(1, true, 123, obj); } export function specialNum() { const a = NaN; const b = Infinity; const c = -Infinity; console.log(a); console.log(b); console.log(c); } ================================================ FILE: tests/samples/builtin_math.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function mathSqrt() { const a = 9; const b = Math.sqrt(a); return b; } export function mathMaxWithOneOperation() { const a = Math.max(3); return a; } export function mathMaxWithMultiOperation() { const a = Math.max(1, 2, 4, 8, 9); return a; } export function mathMinWithOneOperation() { const a = Math.min(2); return a; } export function mathMinWithMultiOperation() { const a = Math.min(1, 2, 4, 8, 9); return a; } export function mathPowWithZero() { const a = Math.pow(3, 0); return a; } export function mathPowWithNegative() { const a = Math.pow(3, -2); return a; } export function mathPowWithPositive() { const a = Math.pow(3, 2); return a; } export function mathAbs() { const a = Math.abs(-3); return a; } export function mathNested() { const a = Math.pow(3, Math.abs(-3)); return a; } ================================================ FILE: tests/samples/builtin_string.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function stringConcat() { const a: string = 'hello'; const b: string = a.concat('world'); return b; } export function stringLength() { const a: string = 'hello'; const b: number = a.length; return b; } export function stringSliceWithTwoNegativeNumber() { const a: string = 'hello'; const b: string = a.slice(-1, -3); return b; } export function stringSliceWithTwoPositiveNumber() { const a: string = 'hello'; const b: string = a.slice(1, 3); return b; } export function stringSliceWithTwoUndefind() { const a: string = 'hello'; const b: string = a.slice(undefined, undefined); return b; } export function stringSliceWithOneZero() { const a: string = 'abcdef'; const b: string = a.slice(0, 2); console.log(b); } export function stringIndexOf() { const a: string = 'helloxxxxkasdfhello'; let x: number = a.indexOf('kasd'); // 9 console.log(x) x = a.indexOf('kh'); // -1 console.log(x) return x; } function outputStrArr(arr: string[]) { for (let i = 0; i < arr.length; i++) { let str = arr[i]; console.log(str) } } export function stringSplit() { let a: string = 'h-e-l-l-o'; let arr: string[] = a.split('-'); // ['h', 'e', 'l', 'l', 'o'] outputStrArr(arr); a = 'hellohe'; arr = a.split('he'); // ['', 'llo', ''] outputStrArr(arr) return arr; } export function stringReplace() { const a: string = 'hellokhello'; // replace longer string -> hellokhello let na: string = a.replace("hellokhelloo", '-'); console.log(na); // replace unmatched string -> hellokhello na = a.replace("pqr", "-"); console.log(na); // replace string hello-hello na = a.replace("k", '-'); console.log(na); // match empty -> -hellokhello na = a.replace("", "-"); console.log(na); // replace with nothing -> hellohello na = a.replace("k", ""); console.log(na); } export function stringMatch() { const str: string = "hello world hello world"; let arr: string[] = str.match("hello"); outputStrArr(arr); // ["hello"] arr = str.match("orld"); outputStrArr(arr); // ["orld"] arr = str.match(""); outputStrArr(arr); // [""] return arr; } export function stringSearch() { const str: string = "hello world hello world"; let idx: number = str.search("hello"); console.log(idx); // 0 idx = str.search("orld"); console.log(idx); // 7 idx = str.search("helloworld"); console.log(idx); // -1 idx = str.search(""); console.log(idx); // 0 return idx; } export function stringcharAt() { const a: string = 'hello world'; let b: string = a.charAt(0); console.log(b); } export function stringtoLowerCase() { const a: string = 'HELLO'; let b: string = a.toLowerCase(); console.log(b); } export function stringtoUpperCase() { const a: string = 'Hello'; let b: string = a.toUpperCase(); console.log(b); } export function stringtrim() { const a: string = ' hello '; let b: string = a.trim(); console.log(b); } export function stringreadonly() { const a: string = 'hello'; let b: string = a[1]; console.log(b); } export function stringadd() { const a: string = 'hello'; const b: string = ' world'; console.log(a + b); } export function stringFromCharCode() { const a = String.fromCharCode(104); console.log(a); const b = String.fromCharCode(105, 106, 107); console.log(b); } ================================================ FILE: tests/samples/call_expression_function_hoisting.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function funcHosting() { const a = foo(); return a; } function foo(a = 10, b = 1, c = 99) { return a + b + c; } ================================================ FILE: tests/samples/call_expression_get_value.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ function callReturnTest(a = 10, b = 1, c = 99) { return a + b + c; } export function getValueWithDefaultParam() { return callReturnTest() + callReturnTest(1, 2, 3); } export function callInnerFunc(a: number, b = 2) { function callReturnTest(a: number, b: number, c: number) { return a + b + c; } const c = callReturnTest(a, b, 3); const d = callReturnTest(1, 2, 3); return c + d; } export function recursive(n: number): number { if (n == 1) { return 1; } if (n == 2) { return 1; } return recursive(n - 2) + recursive(n - 1); } ================================================ FILE: tests/samples/call_expression_param.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ function callReturnTest1(a = 10, b = 1, c = 99) { return a + b + c; } function callReturnTest2(a: number, b = 2) { const c = a + b; return c; } function callReturnTest3(a: number, b: number, c: number) { return a + b + c; } function callReturnTest4(a: number = 5, b: any = 8) { return (a + b) as number; } export function noDefaultParam() { return callReturnTest3(1, 2, 3); } export function allDefaultParam() { const a = callReturnTest1(1, 2, 3); return a; } export function someDefaultParam() { return callReturnTest2(2, 3); } export function someDefaultParamWithAny() { return callReturnTest4(9); } function outer(a: any) { function inner(b: number) { return (a as number) + b; } return inner; } export function paramIsAny() { const res = outer(1)(2); return res; } ================================================ FILE: tests/samples/cast_any_to_static.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function castAnyBackToClass() { const obj1 = { a: 1, b: true, // c: 'hi', }; const objAny: any = obj1; const res = objAny as typeof obj1; return res.b; } export function castAnyBackToString() { const str = 'hi'; const a: any = str; return a as string; } export function castAnyBackToNumber() { const v = false; let a: any = v; const num = 1; a = num; return a as number; } export function castAnyBackToNull() { const extrefIden = null; const a: any = extrefIden; return a as null; } export function castAnyBackToBoolean() { const extrefIden = true; const a: any = extrefIden; return a as boolean; } export function castAnyBackToUndefined() { const extrefIden = undefined; const a: any = extrefIden; return a as undefined; } ================================================ FILE: tests/samples/class_accessor.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A1 { _ref: number; constructor(ref_value: number) { this._ref = ref_value; } set ref(value: number) { this._ref = value; } get ref(): number { return this._ref; } } export function test1() { const instanceA1 = new A1(1); instanceA1.ref = 2; console.log(instanceA1.ref); } class A2 { _ref: number; constructor(ref_value: number) { this._ref = ref_value; } set ref(value: number) { this._ref = value; } } export function test2() { const instanceA2 = new A2(1); instanceA2.ref = 2; console.log(instanceA2.ref); } class A3 { _ref: number; constructor(ref_value: number) { this._ref = ref_value; } get ref(): number { return this._ref; } } export function test3() { const instanceA3 = new A3(1); console.log(instanceA3.ref); } class B1 extends A1 { constructor(ref_value: number) { super(ref_value); } } export function test4() { const instanceB1 = new B1(1); instanceB1.ref = 2; console.log(instanceB1.ref); } class B2 extends A2 { constructor(ref_value: number) { super(ref_value); } } export function test5() { const instanceB2 = new B2(1); instanceB2.ref = 2; console.log(instanceB2.ref); } class C1 extends A1 { constructor(ref_value: number) { super(ref_value); } set ref(value: number) { this._ref = value; } get ref(): number { return this._ref; } } export function test6() { const instanceC1 = new C1(1); instanceC1.ref = 2; console.log(instanceC1.ref); } class C2 extends A2 { constructor(ref_value: number) { super(ref_value); } set ref(value: number) { this._ref = value; } get ref(): number { return this._ref; } } export function test7() { const instanceC = new C2(1); instanceC.ref = 2; console.log(instanceC.ref); } class C3 extends A3 { constructor(ref_value: number) { super(ref_value); } set ref(value: number) { this._ref = value; } get ref(): number { return this._ref; } } export function test8() { const instanceC3 = new C3(1); instanceC3.ref = 2; console.log(instanceC3.ref); } class D1 extends A1 { constructor(ref_value: number) { super(ref_value); } set ref(value: number) { this._ref = value; } } export function test9() { const instanceD1 = new D1(1); instanceD1.ref = 2; console.log(instanceD1.ref); } class D2 extends A2 { constructor(ref_value: number) { super(ref_value); } set ref(value: number) { this._ref = value; } } export function test10() { const instanceD2 = new D2(1); instanceD2.ref = 2; console.log(instanceD2.ref); } class D3 extends A3 { constructor(ref_value: number) { super(ref_value); } set ref(value: number) { this._ref = value; } } export function test11() { const instanceD3 = new D3(1); instanceD3.ref = 2; console.log(instanceD3.ref); } class E1 extends A1 { constructor(ref_value: number) { super(ref_value); } get ref(): number { return this._ref; } } export function test12() { const instanceE1 = new E1(1); console.log(instanceE1.ref); } class E2 extends A2 { constructor(ref_value: number) { super(ref_value); } get ref(): number { return this._ref; } } export function test13() { const instanceE = new E2(1); console.log(instanceE.ref); } class E3 extends A3 { constructor(ref_value: number) { super(ref_value); } get ref(): number { return this._ref; } } export function test14() { const instanceE3 = new E3(1); console.log(instanceE3.ref); } interface I1 { _ref: number; set ref(value: number); } interface I2 { _ref: number; get ref(): number; } interface I3 { _ref: number; set ref(value: number); get ref(): number; } class X1 implements I1 { _ref: number; constructor(ref_value: number) { this._ref = ref_value; } set ref(value: number) { this._ref = value; } } class X2 implements I1 { _ref: number; constructor(ref_value: number) { this._ref = ref_value; } set ref(value: number) { this._ref = value; } get ref(): number { return this._ref; } } class Y1 implements I2 { _ref: number; constructor(ref_value: number) { this._ref = ref_value; } get ref(): number { return this._ref; } } class Y2 implements I2 { _ref: number; constructor(ref_value: number) { this._ref = ref_value; } set ref(value: number) { this._ref = value; } get ref(): number { return this._ref; } } class Z implements I3 { _ref: number; constructor(ref_value: number) { this._ref = ref_value; } set ref(value: number) { this._ref = value; } get ref(): number { return this._ref; } } export function test15() { let i1: I1 = new X1(1); i1.ref = 2; console.log(i1.ref); i1= new X2(1); i1.ref = 2; console.log(i1.ref); let i2: I2 = new Y1(1); console.log(i2.ref); i2= new Y2(1); console.log(i2.ref); const i3: I3 = new Z(1); i3.ref = 2; console.log(i3.ref); } class OnlySetter { a = 1; constructor(a: number) { this.a = a; this.foo(); this.bar(); } foo() { console.log('invoke foo'); } set value(a: number) { this.a = a; } bar() { console.log('invoke bar'); } } export function testOnlySetter() { const obj = new OnlySetter(10); console.log(obj.a); obj.a = 100; console.log(obj.a); } class OnlyGetter { a = 1; constructor(a: number) { this.a = a; this.foo(); this.bar(); } foo() { console.log('invoke foo'); } get value() : number { return this.a; } bar() { console.log('invoke bar'); } } export function testOnlyGetter() { const obj = new OnlyGetter(10); console.log(obj.value); obj.a = 100; console.log(obj.value); } ================================================ FILE: tests/samples/class_basic.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A1 { // empty constructor test() { return 123; } test2() { return 1; } } class A2 { public _a: number; constructor(a: number) { this._a = a; } public testFunc() { this._a = 10; } get a() { return this._a; } set a(m: number) { this._a = m; } } export function withoutCtor() { let a: A1 = new A1(); let b = a.test(); return b; } export function basic() { let a: A2 = new A2(10); return a.a; } class A9 { public _a: number; constructor(a: number) { this._a = a; } set a(m: number) { this._a = m; } get a() { return this._a; } test(m: number) { return m; } test1() {} } export function getterSetter() { let a: A9 = new A9(10); let i = a._a; a.a = i; let j = a.test(5); let k = a.a; return i + j + k; } // class with any type fields class A3 { public _a: any; constructor(a: any) { this._a = a; } public testFunc() { this._a = 10; } get a() { return this._a; } set a(m: any) { this._a = m; } } export function anyType() { const a: A3 = new A3(10); return a.a as number; } class Base { _arg: number; _arg1: number; constructor(arg: number, arg1: number) { this._arg = arg; this._arg1 = arg1; } test() { return this._arg + this._arg1; } } class Derived extends Base { // } export function defaultCtor() { const a = new Derived(1, 2); return a.test(); } class Test { foo(x: number) { return function bar(y: number) { return function baz(z: number) { return x + y + z; }; }; } static bar(x: number) { return function baz(y: number) { return function foo(z: number) { return x + y + z; }; }; } baz() { return new Derived(1, 2); } static baz1() { return new Derived(1, 2); } } export function classNestCall() { const t = new Test(); return ( t.foo(1)(2)(3) + Test.bar(1)(2)(3) + t.baz().test() + Test.baz1().test() + t.baz()._arg + Test.baz1()._arg ); // 20 } /** this as free variable */ class Foo { id: number; constructor(idd: number) { this.id = idd; } } class Bar extends Foo { name: string; constructor(idd: number, namee: string) { super(idd); this.name = namee; } test(addr: number) { const b = () => { if (this.name === 'foo') { return this.id + addr; } this.id++; return this.id; }; this.id++; return b; } } export function thisAsFreeVar() { const b = new Bar(11, 'foo'); const c = b.test(10); console.log(c()); // 22 } export function classInClosure() { class A { foo() { console.log(1); } } class B { x: number; constructor(x: number) { this.x = x; } bar() { console.log(this.x); } } const a = new A(); a.foo(); const b = new B(2); b.bar(); } class C { _onclick: () => void; constructor() { this._onclick = () => {console.log('')}; } set onclick(value: () => void) { this._onclick = value; } get onclick(): () => void { return this._onclick; } } export function test() { const c = new C(); c.onclick = () => {console.log('click')}; const click = c.onclick; click(); } class A { test() { console.log('test'); } } export function methodCallAndGet() { const a = new A; const test = a.test; test(); console.log(test); } ================================================ FILE: tests/samples/class_direct_call.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A { test() { console.log('a'); } } class B extends A { sayHi() { console.log('hi'); } test() { console.log('b'); } } export function foo() { let n = 5; let a = new A(); if (n > 10) { a = new B(); } a.test() n = 18; a = new A(); if (n > 10) { a = new B(); } a.test(); } class Foo { method_foo1(arr: number[]): number { return 1; } method_foo2(a: number, ...b: number[]) { const c = a + b[0] + b[1]; return c; } method_foo3(a: number, ...b: number[]) { return a; } method_foo4(...num: number[]) { let res = 0; for (let i = 0; i < num.length; i++) { res += num[i]; } return res; } } function func_foo(arr: number[]): number { return 2; } export function test() { const a = new Foo(); const arr: number[] = [1, 3, 5, 7]; console.log(a.method_foo1(arr)); console.log(a.method_foo2(1, 2, 3, 4) + a.method_foo3(5)); console.log(a.method_foo4(1, 2)); console.log(a.method_foo4()); console.log(func_foo(arr)); } ================================================ FILE: tests/samples/class_extend.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A6 { public _a: number; public _b: number; constructor(a: number, b: number) { this._a = a; this._b = b; } } class B6 extends A6 { _c: number; constructor(a: number, b: number, c: number) { super(a, b); this._c = c; } } export function extendWithNewProp() { let a: A6 = new B6(10, 20, 30); let res = a._a + a._b; let b = new B6(10, 20, 30); res += b._a + b._b + b._c; return res; } class A7 { public _a: number; constructor(a: number) { this._a = a; } set a(m: number) { this._a = m; } get a() { return this._a; } test(m: number) { return 10; } } class B7 extends A7 { constructor(a: number) { super(a); } test(m: number) { return m; } set a(m: number) { this._a = m; } get a() { return this._a; } } export function methodOverwrite() { let a: A7 = new B7(10); a.a = 20; let b = new B7(10); b.a = 20; return a.a + b.a; } class Base { init(): void { console.log('Base'); } } class A extends Base { init2(): void { console.log('A'); } } class B extends A { init(): void { console.log('B'); } } export function multiLevelExtend() { const b: B = new B(); b.init(); } class Base1 { x: number = 1; foo() { } y: string = '100'; } class ExtendReordered extends Base1 { z: number = 12; y: string = 'Hello'; x: number = 10; } export function testExtendReordered() { const e = new ExtendReordered(); return e.x + e.z; // 22 } class InheritGetter extends B7 { } export function testInheritGetter() { const a = new InheritGetter(10); return a.a; // 10 } /** class cast by using as keyword */ class Baz { constructor() { // } base() { console.log('base'); } } class Foo extends Baz { constructor() { super(); } foo() { console.log('foo'); } } class Bar extends Foo { constructor() { super(); } bar() { console.log('bar'); } } export function inheritCast() { const b = new Bar(); // upcast const f: Foo = b as Foo; const bs: Baz = b as Baz; f.foo(); bs.base(); // downcast const f1 = bs as Foo; const b1 = bs as Bar; f1.foo(); b1.bar(); } interface I { x: number; y: string; func1: () => number; } interface IA { z: boolean; func2: () => boolean; } class A8 { z: boolean; constructor(zz: boolean) { this.z = zz; } } class C extends A8 implements I { x: number; z: boolean; y: string; constructor(xx: number, zz: string, yy: boolean) { super(yy); this.x = xx; this.y = zz; this.z = yy; } func1() { return this.x; } } export function extendsImpl() { const c = new C(1, '23', true); const i: I = c; console.log(i.y === c.y); console.log(i.func1()); const a: A8 = c; console.log(a.z === c.z); } class D implements I, IA { x: number; y: string; z: boolean; constructor(xx: number, yy: string, zz: boolean) { this.x = xx; this.y = yy; this.z = zz; } func1() { return this.x; } func2() { return this.z; } } export function implInfc() { const d = new D(1, '23', false); const i: I = d; console.log(i.func1()); const ia: IA = d; console.log(ia.func2()); } class Car { price = 0; constructor(p: number) { this.price = p; } } class Bus extends Car { price = 100; constructor() { super(-1); } } export function fieldInitOrder() { const b = new Bus(); return b.price; } interface I9 { x: string; } class A9 { x: string; constructor(xx?: I9) { if (xx) { this.x = xx.x; } else { this.x = 'uninit'; } } } class Derived extends A9 { constructor(xx: I9) { super(xx); } } export function superWithOptionlParam() { const d = new Derived({x: 'hello'}); console.log(d.x); } ================================================ FILE: tests/samples/class_field_assign.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A11 { a: number = 10; constructor(a1: number) { this.a = a1; this.b = true; } b = false; c = 'c'; } export function withCtor() { let a: A11 = new A11(18); return a.a; } class A12 { a = 10; b = false; c = 'c'; } export function withoutCtor() { const a = new A12(); return a.b; } const enum Count { ZERO, ONE, TWO, } class A13 { count: Count; constructor() { this.count = Count.ONE; } } export function withEnum() { const a = new A13(); return a.count; } ================================================ FILE: tests/samples/class_field_is_array.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class ParamsObject { key = '123'; val = ''; } class RouteInfo { params: Array = []; } export function test() { let route: RouteInfo = new RouteInfo(); route.params.push(new ParamsObject()); const a = route.params[0]; } ================================================ FILE: tests/samples/class_static_prop.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A15 { static test() { return 1; } // eslint-disable-next-line @typescript-eslint/no-empty-function constructor() {} } class B15 extends A15 { constructor() { super(); } static test() { return 2; } } export function staticMethodWithOverwrite() { return B15.test(); } export class A16 { static hi() { return 1; } } export function staticMethod() { return A16.hi(); } class A { static c = 10; //10 static readonly d = 12 + A.c; //22 } class B extends A { static c = 20; // 20 20 } export function staticFields() { return A.c + A.d + B.c + B.d; } /* extends static fields */ export class A1 { static field1: string = 'field1' static field2: string = 'field2' static field3: string = 'field3' static field4: string = 'field4' } export class A2 extends A1 { static field5: string = 'field5' static field6: string = 'field5' } /* overwrite static fields */ export class A3 { static field1: string = 'field1' static field2: string = 'field2' static field3: string = 'field3' static field4: string = 'field4' } export class A4 extends A2 { static field1: string = 'field5' static field2: string = 'field5' } export class staticFieldsInit { static field1: number = 0 static field2: number = 1 static field3: number = 2 } export function testStaticField1() { return staticFieldsInit.field1; // 0 } export function testStaticField2() { return staticFieldsInit.field2; // 1 } export function testStaticField3() { return staticFieldsInit.field3; // 2 } class Circle { pi: number = 100; static pi: number = 3.14; calculateArea(radius:number):number { // this function has no practical meaning, just for testing. return this.pi * Circle.pi * radius; } static calculateArea(radius:number) { return Circle.pi * radius * radius; } } export function staticFieldWithOverwrite() { console.log(Circle.pi); console.log(Circle.calculateArea(1)); const circle = new Circle(); console.log(circle.pi); console.log(circle.calculateArea(1)); } ================================================ FILE: tests/samples/class_type.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A17 { test() { return 1; } } export function uniqueType() { const a = new A17(); { class A17 { // } const b = a.test(); if (b > 0) { return b; } } return -1; } ================================================ FILE: tests/samples/class_vtable_accessor.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A { _ref: number; get ref(): number { return this._ref; } set ref(value: number) { this._ref = value; } constructor(ref_value: number) { this._ref = ref_value; } } class B extends A { printRef() { console.log(this.ref); // vtable get } setRef() { this.ref = 888; // vtable set } constructor(ref_value: number) { super(ref_value); } } class C extends B { private _C_ref = 100; set ref(value: number) { this._ref = value; } get ref(): number { return this._C_ref; } constructor(ref_value: number) { super(ref_value); } } export function vtableAccessor() { const instance = new B(90); instance.printRef(); instance.setRef(); instance.printRef(); } ================================================ FILE: tests/samples/class_vtable_call.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A { test() { console.log('a'); } } class B extends A { sayHi() { console.log('hi'); } test() { console.log('b'); } } function foo(n: number) { let a = new A(); if (n > 10) { a = new B(); } return a; } export function bar() { let a = foo(1); a.test(); a = foo(20); a.test(); } ================================================ FILE: tests/samples/closure_basic.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ function closure1(x: number, y: boolean) { let z = 1; function inner(z: number) { function inner1(a: number) { const m = 1; return m + z + a + x; // 4 } return inner1; } return inner; } export function accessOuterVars() { const f1 = closure1(1, false); const f2 = f1(1); const res = f2(1); return res; } function closure2(x: number, y: boolean) { let z = 1; z += 10; function inner() { z = 10; return z; } return inner; } export function returnOuterFuncCall() { const f1 = closure2(1, false); return f1(); } export function returnOuterFuncCall2() { return closure1(1, false)(1)(1); // 4 } let y = '123'; export function accesssGlobalVar() { function inner1() { return y; } return inner1; } class A { x: (m: number) => number; constructor(xx: (m: number) => number) { this.x = xx; } } class B { y = 'hello'; } class AA { x: (m: number) => number; y = (m: number, b: B) => { if (b.y === 'hello') { return m + 1; } return m; }; constructor(xx: (m: number) => number) { this.x = xx; } } function foo() { const m = 10; const param = (x: number) => { return x + m; }; return param; } export function classFieldIsClosure() { const a = new A(foo()); console.log(a.x(10)); } export function classFieldIsClosureWithDefault() { const a = new AA(foo()); const b = new B(); console.log(a.x(10) + a.y(20, b)); } export function nullable_closure(): any | null { const handler: ((events: any) => any) | null = (a) => { console.log(a); return a; }; if (handler) { return handler('hello'); } } ================================================ FILE: tests/samples/closure_first_class_func.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ function closure(x: number) { let z = 1; function inner1() { return z + 1; } function inner2() { return z + 2; } if (x > 10) { return inner1; } return inner2; } export function returnAFunction() { const f1 = closure(1); return f1(); } function foo(y: number) { return y; } function FirstClassFuncClosureCase2(x: (y: number) => number) { let a = 10; let z = x(a); return z; } export function functionAsParam() { let y = FirstClassFuncClosureCase2(foo); return y; } ================================================ FILE: tests/samples/closure_inner_func.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function outerFunc() { let a = 1; function inner() { console.log(a); a--; } function inner2() { a++; inner(); console.log(a); } inner2(); } function outerWithBlock(a:number) { if (a > 1) { function inner() { console.log(a + 50); } function inner2() { return inner; } return inner2(); } else { function inner() { console.log(a + 100); } function inner2() { return inner; } return inner2(); } } export function outerFuncWithBlock() { outerWithBlock(2)(); outerWithBlock(0)(); } ================================================ FILE: tests/samples/closure_set_ctx_value.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function setCtxValue(m: number) { function test2(x: number) { let y = 10 + m; const func = function () { const z = x + y++; return z; }; x++; return func; } const x = test2(10); const y = x(); return y; } ================================================ FILE: tests/samples/comments_not_entry.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ // Wasmnizer-ts: @Export@ nameBNotInEntry export function nameANotInEntry() { console.log('exportName is nameBNotInEntry'); } ================================================ FILE: tests/samples/comments_with_export.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { nameANotInEntry } from "./comments_not_entry"; // Wasmnizer-ts: @Export@ nameB export function nameA() { console.log('exportName is nameB'); } // Wasmnizer-ts: @Export@ nameD // Wasmnizer-ts: @NativeSignature@ (i32, i32)=>void export function nameC(arrayBuffer: ArrayBuffer, length: i32) { console.log('exportName is nameD'); } ================================================ FILE: tests/samples/comments_with_import.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ // Wasmnizer-ts: @Export@ nameF // Wasmnizer-ts: @NativeSignature@ (i32, i32)=>i32 export declare function nameE(arrayBuffer: ArrayBuffer, length: i32): i32; // Wasmnizer-ts: @Import@ wamr, nameH // Wasmnizer-ts: @NativeSignature@ (i32, i32)=>void declare function nameG(buffer: ArrayBuffer, size: i32): void; export function callDeclare() { const a = new ArrayBuffer(10); nameG(a, 10); } ================================================ FILE: tests/samples/complexType_case1.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ // const cpxCase1Var1 = [1, 2, 3]; // const cpxCase1Var2 = [2, 'hi']; // const cpxCase1Var3 = [[2, 3], ['hi']]; // const cpxCase1Var4 = [{ a: 1 }, { a: 2 }, { a: 3, b: 3 }]; // const cpxCase1Var5 = [{ a: 1 }, { a: 2 }]; // const cpxCase1Var6 = new Array(2); // // class cpxCase1Class1 {} // // const cpxCase1Var7 = [new cpxCase1Class1(), new cpxCase1Class1()]; // function cpxCase1Func1(a: number) { // return 1; // } // const cpxCase1Var8 = [cpxCase1Func1]; // function cpxCase1Func2(a: number) { // return 'hi'; // } // const cpxCase1Var9 = [cpxCase1Func1, cpxCase1Func2]; // const cpxCase1Var10 = { a1: 2, b: 'hello' }; // const cpxCase1Var11 = { a: 2, b2: 'hello' }; export function complexTypeTest() { const cpxCase1Var4 = [{ a: 1 }, { a: 2 }, { a: 3, b: 3 }]; const cpxCase1Var5 = [{ a: 1 }, { a: 2 }]; return cpxCase1Var4[2].a + cpxCase1Var5[1].a; } ================================================ FILE: tests/samples/complexType_case2.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ const cpxCase2Var1 = [1, 2, 3]; const cpxCase2Var2 = [2, 'hi']; const cpxCase2Var5 = [{ a: 1 }, { a: 2 }]; const cpxCase2Var6 = new Array(2); // class cpxCase2Class1 {} // const cpxCase2Var7 = [new cpxCase2Class1(), new cpxCase2Class1()]; function cpxCase2Func1(a: number) { return 1; } const cpxCase2Var8 = [cpxCase2Func1]; function cpxCase2Func2(a: number) { return 'hi'; } export function cpxCase2Func3(a: number) { let b = 2; { const cpxCase2Var3 = [[2, 3], ['hi']]; const cpxCase2Var4 = [{ a: 1 }, { a: 2 }, { a: 3, b: 3 }]; const innerBlock = 1; if (a > b) { return 2; } else { for (let i = 0, j = 'hi'; i < a; i++) { b++; const cpxCase2Var12 = [[1]]; } const cpxCase2Var9 = [cpxCase2Func1, cpxCase2Func2]; const cpxCase2Var10 = { a1: 2, b: 'hello' }; const cpxCase2Var11 = { a: 2, b2: 'hello' }; } } } ================================================ FILE: tests/samples/complexType_case3.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class cpxCase3Class1 { x = 10; } export function cpxCase3Func1() { const a: cpxCase3Class1 = new cpxCase3Class1(); return a.x; } ================================================ FILE: tests/samples/complexType_case4.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class cpxCase3Class1 { foo(i: number, j: number) { return i + j; } } export function cpxCase3Func1() { const a: cpxCase3Class1 = new cpxCase3Class1(); let k = a.foo(1, 2); return k; } ================================================ FILE: tests/samples/complexType_case5.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A { test() { return 1; } } export function cpxCase3Func1() { const arr = [new A(), new A(), new A()]; const arr2 = [arr]; const yyy = arr2[arr[0].test() - 1][2].test() + 5; return yyy; } ================================================ FILE: tests/samples/compound_assignment_operator.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A { x = 10; } export function test() { let a = 1; a += 2; console.log(a); const b: number[] = [1, 2, 3]; b[1] += b[1] * 3; console.log(b[1]); const c = new A(); c.x -= c.x / 2; console.log(c.x); let d = 'hello'; d += ' world'; console.log(d); } ================================================ FILE: tests/samples/dataview_basic.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function getDataViewProperty() { const a = new ArrayBuffer(10); const d = new DataView(a, 1, 5); console.log(d.byteLength); console.log(d.byteOffset); } export function newDataView() { const a = new ArrayBuffer(10); const d1 = new DataView(a); d1.setInt8(0, 5); console.log(d1.getInt8(0)); const d2 = new DataView(a, 5); d2.setInt8(0, -5); console.log(d2.getInt8(0)); console.log(d1.byteLength); console.log(d1.byteOffset); console.log(d2.byteLength); console.log(d2.byteOffset); } export function dataViewI8() { const a = new ArrayBuffer(10); const d = new DataView(a, 1, 5); console.log(d.getInt8(2)); // 0 d.setInt8(2, 5); console.log(d.getInt8(2)); // 5 d.setInt8(2, -5); console.log(d.getInt8(2)); // -5 } export function dataViewI16() { const a = new ArrayBuffer(16); const d = new DataView(a, 3, 8); d.setInt16(2, 5, true); console.log(d.getInt8(2)); // 5 console.log(d.getInt8(3)); // 0 console.log(d.getInt16(2, true)); // 5 console.log(d.getInt16(2)); // 1280 d.setInt16(2, 5); console.log(d.getInt8(2)); // 0 console.log(d.getInt8(3)); // 5 console.log(d.getInt16(2, true)); // 1280 console.log(d.getInt16(2, false)); // 5 d.setInt16(2, -5, true); console.log(d.getInt8(2)); // -5 console.log(d.getInt8(3)); // -1 console.log(d.getInt16(2, true)); // -5 console.log(d.getInt16(2)); // -1025 d.setInt16(2, -5); console.log(d.getInt8(2)); // -1 console.log(d.getInt8(3)); // -5 console.log(d.getInt16(2, true)); // -1025 console.log(d.getInt16(2, false)); // -5 } export function dataViewI32() { const a = new ArrayBuffer(16); const d = new DataView(a, 3, 8); d.setInt32(2, 5, true); console.log(d.getInt16(2, true)); // 5 console.log(d.getInt16(2)); // 1280 console.log(d.getInt32(2, true)); // 5 console.log(d.getInt32(2)); // 83886080 d.setInt32(2, 5); console.log(d.getInt16(2, true)); // 0 console.log(d.getInt16(2)); // 0 console.log(d.getInt32(2, true)); // 83886080 console.log(d.getInt32(2)); // 5 d.setInt32(2, -5, true); console.log(d.getInt16(2, true)); // -5 console.log(d.getInt16(2)); // -1025 console.log(d.getInt32(2, true)); // -5 console.log(d.getInt32(2)); // -67108865 d.setInt32(2, -5); console.log(d.getInt16(2, true)); // -1 console.log(d.getInt16(2)); // -1 console.log(d.getInt32(2, true)); // -67108865 console.log(d.getInt32(2)); // -5 } export function dataViewUi8() { const a = new ArrayBuffer(10); const d = new DataView(a, 1, 5); d.setUint8(2, 5); console.log(d.getInt8(2)); // 5 console.log(d.getUint8(2)); // 5 d.setUint8(2, -5); console.log(d.getInt8(2)); // -5 console.log(d.getUint8(2)); // 251 d.setInt8(2, 5); console.log(d.getInt8(2)); // 5 console.log(d.getUint8(2)); // 5 d.setInt8(2, -5); console.log(d.getInt8(2)); // -5 console.log(d.getUint8(2)); // 251 } export function dataViewUi16() { const a = new ArrayBuffer(10); const d = new DataView(a, 1, 5); d.setUint16(2, 5); console.log(d.getInt16(2)); // 5 console.log(d.getUint16(2)); // 5 d.setUint16(2, -5); console.log(d.getInt16(2)); // -5 console.log(d.getUint16(2)); // 65531 d.setInt16(2, 5); console.log(d.getInt16(2)); // 5 console.log(d.getUint16(2)); // 5 d.setInt16(2, -5); console.log(d.getInt16(2)); // -5 console.log(d.getUint16(2)); // 65531 } export function dataViewUi32() { const a = new ArrayBuffer(10); const d = new DataView(a, 1, 8); d.setUint32(2, 5); console.log(d.getInt32(2)); // 5 console.log(d.getUint32(2)); // 5 d.setUint32(2, -5); console.log(d.getInt32(2)); // -5 console.log(d.getUint32(2)); // 4294967291 d.setInt32(2, 5); console.log(d.getInt32(2)); // 5 console.log(d.getUint32(2)); // 5 d.setInt32(2, -5); console.log(d.getInt32(2)); // -5 console.log(d.getUint32(2)); // 4294967291 } export function dataViewF32() { const a = new ArrayBuffer(16); const d = new DataView(a, 0, 8); d.setFloat32(0, 2.25, true); console.log(d.getInt32(0, true)); // 1074790400 console.log(d.getFloat32(0, true)); // 2.25 d.setFloat32(0, -0.75, true); console.log(d.getInt32(0, true)); // -1086324736 console.log(d.getFloat32(0, true)); // -0.75 } export function dataViewF64() { const a = new ArrayBuffer(16); const d = new DataView(a, 0, 8); d.setFloat64(0, 2.25, true); console.log(d.getInt32(0, true)); // 0 console.log(d.getFloat32(0, true)); // 0 console.log(d.getFloat64(0, true)); // 2.25 d.setFloat64(0, -0.75); console.log(d.getInt32(0)); // -1075314688 console.log(d.getFloat32(0)); // -1.8125 console.log(d.getFloat64(0)); // -0.75 } ================================================ FILE: tests/samples/decimalization.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function initNumberWithBinary() { let value = 0b00101; // 5 console.log(value); value = 0B001010; // 10 console.log(value); } export function initNumberWithOctal() { let value = 0O000171; // 64 + 56 + 1 = 121 console.log(value); value = 0o0001710; // 968 console.log(value); } export function initNumberWithHex() { let value = 0x00AF1; // 10 * 16^2 + 15 * 16 + 1 = 2801 console.log(value); value = 0X00AF0; // 2800 console.log(value); value = 0X00af0; // 2800 console.log(value); } ================================================ FILE: tests/samples/declare_class.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ declare class DeclaredClass { grade: number; constructor(grade: number); sayHello(): void; static whoSayHi(name: string): number; get value(): any; set value(v: number); } export function classDecl() { const sayHiFunc = DeclaredClass.whoSayHi('i'); console.log(sayHiFunc); const dc = new DeclaredClass(99); console.log(dc.grade); dc.value = 100; console.log(dc.value); dc.sayHello(); } ================================================ FILE: tests/samples/declare_func.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ declare function noParamNoReturn(): void; declare function noParamWithReturn(): number; declare function withParamNoReturn(x: number): void; declare function withParamWithReturn(x: number): number; /* Assigned to global variable */ let f1 = noParamNoReturn; f1(); let f2 = noParamWithReturn; let res = f2(); let f3 = withParamNoReturn; f3(5); let f4 = withParamWithReturn; res = f4(10); /* Assigned to local variables */ export function assignDeclareFuncToVar() { let f1 = noParamNoReturn; f1(); let f2 = noParamWithReturn; let res = f2(); let f3 = withParamNoReturn; f3(5); let f4 = withParamWithReturn; res = f4(10); } /* Direct call */ noParamNoReturn(); res = noParamWithReturn(); withParamNoReturn(5); res = withParamWithReturn(10); ================================================ FILE: tests/samples/declare_namespace.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ declare namespace DeclaredNS { const a: number; function nsFunc(): void; } ================================================ FILE: tests/samples/declare_var.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ declare const declaredVar: number; ================================================ FILE: tests/samples/default_param.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A { add(x: number, y: number, z = 5) { return x + y + z; } static add_static(x: number, y: number, z = 5) { return x + y + z; } } export function defaultParamInMethod() { const a = new A(); console.log(a.add(1, 2)); } export function defaultParamInStaticMethod() { const a = new A(); console.log(A.add_static(1, 2)); } function add(x: number, y: number, z = 5) { return x + y + z; } export function defaultParamInFunction() { console.log(add(1, 2)); } /* Hint: closure call's default param is not supported */ ================================================ FILE: tests/samples/enum.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ enum Color1 { RED = 'red', GREEN = 'green', BLUE = 'blue', WHITE = 0, } enum Color2 { RED, GREEN, BLUE, WHITE = "white" } enum Color3 { RED = 1, GREEN, BLUE, WHITE = 0, PINK, } export function assignDigitMember() { let red: Color2 = Color2.RED; if (red === Color2.RED) { console.log(red); } } export function assignStringMember() { let red: Color1 = Color1.RED; if (red === Color1.RED) { console.log(red); } } export function enumInitialize() { console.log(Color3.BLUE); console.log(Color3.PINK); } enum A { a, b, } enum B { a = "a", b = "b", } function foo(a: A): B { if(a == A.a) return B.a; return B.b } export function enumParamter() { console.log(foo(A.a)); console.log(foo(A.b)); } ================================================ FILE: tests/samples/exception_catch_error.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ function throwNewError() { throw new Error('A new error'); } export function noThrow() { let a = 1; try { throw 'hh'; a += 10; } finally { a += 100; console.log('execute in finally'); throw 'iii' return a; } return a; } export function throwErrorInTry() { let a = 1; try { a += 1; throw new Error('throw error in try'); } catch (e) { a += 2; } finally { a += 3; } return a; } export function throwErrorInFunc() { let a = 1; try { a += 1; throwNewError(); } catch (e) { a += 2; } finally { a += 3; } return a; } export function throwErrorInCatch() { let a = 1; try { a += 1; } catch (e) { a += 2; throw new Error('throw error in catch'); } finally { a += 3; } return a; } export function throwErrorInFinally() { let a = 1; try { a += 1; } catch (e) { a += 2; } finally { a += 3; throw new Error('throw error in finally'); } return a; } export function throwErrorInTryAndCatch() { let a = 1; try { a += 1; throw new Error('throw error in try'); } catch (e) { a += 2; throw new Error('throw error in catch'); } finally { a += 3; } return a; } export function throwErrorInTryAndFinally() { let a = 1; try { a += 1; throw new Error('throw error in try'); } catch (e) { a += 2; } finally { a += 3; throw new Error('throw error in finally'); } return a; } export function throwErrorInTryAndCatchAndFinally() { let a = 1; try { a += 1; throw new Error('throw error in try'); } catch (e) { a += 2; throw new Error('throw error in catch'); } finally { a += 3; throw new Error('throw error in finally'); } return a; } export function nestedThrowError() { let a = 1; try { a += 1; try { a += 1; throw new Error('throw error in try'); } catch (e) { a += 2; throw new Error('throw error in catch'); } finally { a += 3; throw new Error('throw error in finally'); } } catch (e) { a += 2; } finally { a += 3; } return a; } ================================================ FILE: tests/samples/exception_custom_error.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class AError extends Error { constructor(message: string) { super(message); } } export function throwCustomError() { let a = 1; try { a += 1; throw new AError('throw custom error A'); } catch (error) { a += 2; } return a; } ================================================ FILE: tests/samples/exception_throw_error.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function throwNewError() { throw new Error('A new error'); } export function throwError() { throw Error('An error'); } ================================================ FILE: tests/samples/exception_try_structure.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function oneTryCatch() { let a = 1; try { a += 1; } catch { a += 2; } return a; } export function oneTryFinally() { let a = 1; try { a += 1; } finally { a += 3; } return a; } export function oneTryCatchFinally() { let a = 1; try { a += 1; } catch { a += 2; } finally { a += 3; } return a; } export function nestedTryCatchInTry() { let a = 1; try { a += 1; try { a += 1; } catch { a += 2; } } catch { a += 2; } return a; } export function nestedTryCatchInCatch() { let a = 1; try { a += 1; } catch { a += 2; try { a += 1; } catch { a += 2; } } return a; } export function nestedTryFinallyInTry() { let a = 1; try { a += 1; try { a += 1; } finally { a += 3; } } finally { a += 3; } return a; } export function nestedTryFinallyInFinally() { let a = 1; try { a += 1; } finally { a += 3; try { a += 1; } finally { a += 3; } } return a; } export function nestedTryCatchFinallyInTry() { let a = 1; try { a += 1; try { a += 1; } catch { a += 2; } finally { a += 3; } } catch { a += 2; } finally { a += 3; } return a; } export function nestedTryCatchFinallyInFinally() { let a = 1; try { a += 1; } catch { a += 2; } finally { a += 3; try { a += 1; } catch { a += 2; } finally { a += 3; } } return a; } ================================================ FILE: tests/samples/export_alias_identifier.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ function oriExportFunc() { return 10; } function exportFunc() { return 20; } const oriExportVar = 100; const exportVar = 200; class OriExportClass { field = 50; getField() { return this.field; } } class ExportClass { field = 600; getField() { return this.field; } } namespace oriExportNS { export function aFunc() { return 88; } } namespace exportNS { export function aFunc() { return 66; } } export { oriExportFunc as exportFunc, oriExportVar as exportVar, OriExportClass as ExportClass, oriExportNS as exportNS, }; ================================================ FILE: tests/samples/export_alias_imported_identifier.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { exportFunc as aliasFunc, exportVar as aliasVar, ExportClass as AliasClass, exportNS as aliasNS, } from './export_alias_identifier'; export { aliasFunc as oriExportFunc, aliasVar as oriExportVar, AliasClass as OriExportClass, aliasNS as oriExportNS, }; ================================================ FILE: tests/samples/export_class.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export class ExportedClass { _a = 8; set a(v: number) { this._a = v; } get a(): number { return this._a; } setA(a: number) { this._a = a; } } export default class DefaultExportClass { public static foo(x: number) { return x + 1; } public bar(x: number) { return x * 2; } } export interface BaseI { x: number; } ================================================ FILE: tests/samples/export_expression_number_literal.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export default 100; ================================================ FILE: tests/samples/export_expression_obj_literal.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export default { a: 2 }; ================================================ FILE: tests/samples/export_from/classB.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import {A as AA} from './libA/index' class B extends AA { b:number; constructor() { super(); this.b = 16; } } export function test() { let b = new B(); console.log(b.a,b.b); } ================================================ FILE: tests/samples/export_from/libA/index.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export {A, testLog} from './lib/classA' ================================================ FILE: tests/samples/export_from/libA/lib/classA.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export class A{ a:number; constructor() { this.a = 14; } } let name:string = "hello" export function testLog(){ console.log(name); } ================================================ FILE: tests/samples/export_func.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export { mulFunc }; export { subFunc as sub, divFunc as divFunc }; export default defaultFunc; function defaultFunc(): number { return 100; } export function addFunc(a: number, b: number) { return a + b; } function subFunc(a: number, b: number) { return a - b; } function mulFunc(a: number, b: number) { return a * b; } function divFunc(a: number, b: number) { return a / b; } ================================================ FILE: tests/samples/export_func2.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { mulFunc, sub, addFunc, divFunc } from "./export_func" import defaultFunc from "./export_func" export { defaultFunc, mulFunc, sub } ================================================ FILE: tests/samples/export_func_invoked.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function beCalledFunc() { return 2; } export function exportedFunc() { return beCalledFunc(); } ================================================ FILE: tests/samples/export_implicit_type.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ const obj_literal: {idx: number, newId: string} = {idx: 1, newId: 'hi'}; export const obj_array = [obj_literal]; ================================================ FILE: tests/samples/export_namespace.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export namespace namespaceA { function aFunc() { return 10; } export function bFunc() { return aFunc(); } } namespace namespaceB { export const aVar = 8; } export default namespaceB; ================================================ FILE: tests/samples/export_re_export.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export { default, globalType, exportTypeNS } from './export_type'; ================================================ FILE: tests/samples/export_type.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export type globalType = string; type globalTypeDefault = number; export default globalTypeDefault; export namespace exportTypeNS { export type innerType = boolean; } export type Pair = { first: T; second: U; }; export type funcType = () => number; export type objLiteralType = { a: string, b: number, } ================================================ FILE: tests/samples/export_var.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export const aVar = 10; export { bVar, cVar as c }; let bVar = aVar; bVar += 100; const cVar = 1000; ================================================ FILE: tests/samples/expression_binary.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function lt() { const a = 5; if (a < 10) { return 1; } return 0; } export function gt() { const a = 5; if (a > 4) { return 1; } return 0; } export function le() { const a = 5; const b = 5; if (a <= 5) { if (b <= 6) { return 1; } } return 0; } export function ge() { const a = 5; const b = 5; if (a >= 5) { if (b >= 4) { return 1; } } return 0; } export function eq() { const a = 5; if (a == 5) { return 1; } return 0; } export function seenAsEq() { const a = 5; if (a === 5) { return 1; } return 0; } export function ne() { const a = 5; if (a != 5) { return 0; } return 1; } export function seenAsNe() { const a = 5; if (a !== 5) { return 0; } return 1; } export function add() { return 1 + 2; } export function sub() { return 2 - 1; } export function mul() { return 2 * 2; } export function div() { return 2 / 2; } export function subEq() { let a = 3; a -= 2; return a; } export function addEq() { let a = 1; a += 2; return a; } export function mulEq() { let a = 3; a *= 2; return a; } export function divEq() { let a = 4; a /= 2; return a; } export function xor() { let x = 4 ^ 0x1234; console.log(x); x = 2147483649 ^ 1; console.log(x); x = -4 ^ -0x1234; console.log(x); x = -2147483649 ^ -1; console.log(x); } export function shl() { const x = 9 << 2; console.log(x); const y = 2147483649 << 1; console.log(y); const z = -1 << 2; console.log(z); const m = -2147483649 << 1; console.log(m); } export function shr() { const x = 9 >> 2; console.log(x); let y = 2147483649 >> 1; console.log(y); const z = -1 >> 2; console.log(z); let m = -2147483649 >> 1; console.log(m); y = 2147483649 >>> 1; console.log(y); m = -2147483649 >>> 1; console.log(m); } export function and() { let x = 0x1234 & 0x2345; console.log(x); x = 2147483649 & 1; console.log(x); x = -0x1234 & -0x2345; console.log(x); x = -2147483649 & -1; console.log(x); } export function or() { let x = 0x1234 | 0x2345; console.log(x); x = 2147483649 | 2147483649; console.log(x); x = -0x1234 & -0x2345; console.log(x); x = -2147483649 & -2147483649; console.log(x); } ================================================ FILE: tests/samples/expression_binary_select.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function selectAmpersandTrueTrue() { const a = 10, b = 20; const condition = a && b; if (condition) { return condition; } return 0; } export function selectAmpersandTrueFalse() { const a = 10, b = 20; const condition = a && !b; if (condition) { return 1; } return 0; } export function selectAmpersandFalseTrue() { const a = 10, b = 20; const condition = !a && b; if (condition) { return 1; } return 0; } export function selectAmpersandFalseFlase() { const a = 10, b = 20; const condition = !a && !b; if (condition) { return 1; } return 0; } export function selectBarTrueTrue() { const a = 10, b = 20; const condition = a || b; if (condition) { return condition; } return 0; } export function selectBarTrueFalse() { const a = 10, b = 20; const condition = a || !b; if (condition) { return 1; } return 0; } export function selectBarFalseTrue() { const a = 10, b = 20; const condition = !a || b; if (condition) { return 1; } return 0; } export function selectBarFalseFalse() { const a = 10, b = 20; const condition = !a || !b; if (condition) { return 1; } return 0; } ================================================ FILE: tests/samples/expression_condition.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function binaryAsCondition() { const a = 10, b = 20; if (a + b) { return 1; } return 0; } ================================================ FILE: tests/samples/expression_unary.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function prefixUnaryPlusPlus() { let a = 1; ++a; a++; return a; } export function prefixUnaryMinusMinus() { let a = 1; --a; a--; return a; } export function prefixUnaryExclamation() { const a = 0; if (!a) { return 1; } return 0; } export function prefixUnaryMinusToLiteralWithBinaryExpr() { let a: number; a = -1; return a; } export function prefixUnaryMinusToLiteralWithVarStmt() { const a = -1; return a; } export function prefixUnaryMinusToVarWithBinaryExpr() { const a = 1; let b: number; b = -a; return b; } export function prefixUnaryMinusToVarWithVarStmt() { const a = 1; const b = -a; return b; } export function prefixUnaryPlus() { let x = 1; const a = +x; return a; } ================================================ FILE: tests/samples/fallback_quickjs.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A { x: number; constructor(xx: number) { this.x = xx; } } class VNode { _data: any; _data2: any = new Map(); static _data3: any = new Map(); constructor() { this._data = new Map(); } } export function mapTest() { const a: any = new Map(); const k: any = 1; const v: any = 2; a.set(k, v); console.log(a.get(1)); // 2 a.clear(); console.log(a.get(1)); // undefined a.set('hello', 'world'); console.log(a.get('hello')); // world a.delete('hello'); console.log(a.get('hello')); // undefined const obj1 = new A(10); const o1: any = obj1; const obj2 = new A(11); const o2: any = obj2; a.set(o1, o2); console.log(a.has(o1)); // true console.log(a.has(o2)); // false const key = a.get(o1) as A; console.log(key.x); // 11 console.log(a.size); //1 if (a.has(o1)) { console.log(1); } if (a.has(o2)) { console.log(2); } const vn = new VNode(); vn._data.set('a', 1); vn._data2.set('a', 1); VNode._data3.set('a', 1); console.log(vn._data.get('a') + vn._data2.get('a') + VNode._data3.get('a')); // 3 } export function setTest() { const tset: any = new Set(); const v1: any = 1; const v2: any = true; tset.add(v1); tset.add(v2); console.log(tset.has(v1)); // true tset.clear(); tset.add('hello'); tset.add('world'); console.log(tset.size); // 2 const obj1 = new A(10); const o1: any = obj1; console.log(o1); // object const obj2 = new A(11); const o2: any = obj2; tset.add(o1); tset.add(o2); console.log(tset.has(o1)); // true console.log(tset.has(o2)); // true console.log(tset.size); // 4 } ================================================ FILE: tests/samples/fallback_quickjs_Date.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ let date = new Date(); //console.log(date.getTime()); //console.log(Date.now()); console.log(Date.parse('2023-7-27')); export function DateTest1() { let date = new Date(2023, 7); console.log(date.getFullYear()); let date1 = new Date('December 17, 1995 03:24:00'); console.log(date1); let date2 = new Date('1995-12-17T03:24:00'); console.log(date2.getFullYear()) let date3 = new Date(2023, 7, 23, 12, 1, 0, 0); console.log(date3.getFullYear()); console.log(Date.parse('2023-7-27')); console.log(Date.UTC(2023, 7, 23)); } export function DateTest2() { let date = new Date(2023, 7); console.log(date.getFullYear()); console.log(Date.parse('2023-7-27')); } ================================================ FILE: tests/samples/fallback_quickjs_JSON.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ let json = '{"result":true, "count":42}'; let obj = JSON.parse(json); let str: string = JSON.stringify(obj) as string; console.log(obj.count); console.log(str); export function JSONTest(){ let json = '{"result":true, "count":42}'; let obj = JSON.parse(json); let str: string = JSON.stringify(obj) as string; console.log(obj.count); console.log(str); } export function JSONTest2(){ let json = '{"result":true, "count":42}'; let obj = JSON.parse(json); let str: string = JSON.stringify(obj) as string; console.log(obj.count); console.log(str); } export function JSONTest3() { let res: any = { "'''\'\n\r\t\\1": "2\n", } let jres1: any = JSON.stringify(res) console.log(jres1) } ================================================ FILE: tests/samples/for_in.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ interface I { [key: string]: number; } interface I_FUNC { [key: string]: () => number; } export function infc_obj_get_field() { const obj: I = { x: 1, y: 2, }; for (const key in obj) { console.log(obj[key]); } } export function infc_obj_set_field() { const obj: I = { x: 1, y: 2, }; for (const key in obj) { obj[key] = 100; console.log(obj[key]); } } export function infc_obj_get_method() { const obj: I_FUNC = { x: () => 1, y: () => 2, }; for (const key in obj) { const a = obj[key]; console.log(a()); } } export function infc_obj_set_method() { const obj: I_FUNC = { x: () => 1, y: () => 2, }; for (const key in obj) { obj[key] = () => 100; const a = obj[key]; console.log(a()); } } class A { a = 1; b = 2; } export function extref_obj() { const obj: any = new A(); for (const key in obj) { const value = obj[key]; console.log(value); obj[key] = 88; console.log(obj[key]); } } export function dynamic_obj() { const obj: any = {a: 1, b: 2}; for (const key in obj) { const value = obj[key]; console.log(value); obj[key] = 88; console.log(obj[key]); } } export function mix_obj() { const a = new A(); const obj: any = a; obj['c'] = 3; for (const key in obj) { const value = obj[key]; console.log(value); obj[key] = 88; console.log(obj[key]); } } export function forInWithContinue() { const expect = ['b', 'd', 'c', 'd', 'e', 'a', 'c', 'd']; const result: string[] = []; let obj: I = { a: 1, b: 2, c: 3, d: 4 }; for (const key in obj) { if (obj[key] % 2 !== 0) { continue; } result.push(key); } obj = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6 }; for (const key in obj) { if (obj[key] < 3) continue; if (obj[key] > 5) continue; result.push(key); } obj = { a: 1, b: 2, c: 3, d: 4 }; for (const key in obj) { if (obj[key] % 2 === 0) { if (obj[key] === 2) continue; result.push(key); } else { result.push(key); } } if (result.length !== expect.length) { return false; } for (let i = 0; i < expect.length; i++) { if (result[i] !== expect[i]) { return false; } } return true; } ================================================ FILE: tests/samples/for_of.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ const arr = [1, 4, 6]; const str = 'abc'; const dynArr: any = [2, 3, 5]; export function forOfForArray(): void { for (const element of arr) { console.log(element); } for (const element of arr) console.log(element); const localArr = arr; for (const element of localArr) { console.log(element); } for (const element of dynArr) { console.log(element); } for (const element of dynArr) console.log(element); const localDynArr = dynArr; for (const element of localDynArr) { console.log(element); } } export function forOfForString() { let char: string; for (char of str) { console.log(char); } } const map: any = new Map(); const set: any = new Set(); map.set('key1', 'value1'); map.set('key2', 'value2'); set.add('value1'); set.add('value2'); let mapKeys = map.keys(); const mapValue = map.values(); const mapEntries = map.entries(); const setValues = set.values(); export function forOfForMapKeys() { for (const element of mapKeys) { console.log(element); } mapKeys = map.keys(); for (const element of mapKeys) console.log(element); const localMapKeys = map.keys(); for (const element of localMapKeys) { console.log(element); } } export function forOfForMapValues() { for (const element of mapValue) { console.log(element); } const localMapValue = map.values(); for (const element of localMapValue) { console.log(element); } } export function forOfForMapEntries() { for (const element of mapEntries) { console.log(element); } const localMapEntries = map.entries(); for (const element of localMapEntries) { console.log(element); } } export function forOfForSetValues() { for (const element of setValues) { console.log(element); } const localSetValues = set.values(); for (const element of localSetValues) { console.log(element); } } export function forOfWithContinue() { const expect = ['1', '3', '5', 'h', 'l', 'l']; const result: string[] = []; const arr = [1, 2, 3, 4, 5]; for (const item of arr) { if (item % 2 === 0) continue; result.push(item.toString()); } const str = 'hello'; for (const char of str) { if (char === 'e' || char === 'o') continue; result.push(char); } if (result.length !== expect.length) { return false; } for (let i = 0; i < expect.length; i++) { if (result[i] !== expect[i]) { return false; } } return true; } ================================================ FILE: tests/samples/func_cast_interface.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ interface MathFunction { (x: number, y: number): number; } function foo(f: MathFunction): MathFunction{ console.log(f(1,1)); return f; } let add: MathFunction = (x, y) => x + y; console.log(add(1, 2)); export function test() { console.log(add(1, 3)); add = (x, y) => x * 2 + y; let multiple: MathFunction = (x, y) => x * y; console.log(multiple(2, 2)); let f = foo(add); console.log(f(2,3)); } ================================================ FILE: tests/samples/function_declaration.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function simpleFunctionOnlyReturn() { return 1; } export function basicFunction(a: number, b: number) { return a + b; } function functionWithDefaultParameter(a = 1, b = 2) { return a + b; } export function defaultParamExport() { return functionWithDefaultParameter(); } export function functionWithFuncScopeVariable() { c = 2; const a = c; // eslint-disable-next-line no-var var c = 3; const b = c; return a + b; } export function miltipleVariablesInOneStatement() { c = 2; const a = c, b = a; // eslint-disable-next-line no-var var c = 6; let d: number; d = 3; return a + b + d; } ================================================ FILE: tests/samples/function_expression.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ const fe_case1_1 = function (a: number, b: number) { return a + b; }; export function functionExpression() { return fe_case1_1(7.1, 1997); } const arrowFunc = (a: number) => { return a + 1 }; export function arrowFunction() { return arrowFunc(1); } const arrowFuncNoReturn = (a: number) => a + 1; const arrowFuncNoReturn1 = (a: number) => console.log(a); export function arrowFunctionWithoutReturn() { arrowFuncNoReturn1(1); return arrowFuncNoReturn(1); } function foo(f: (x: number, y: number) => number): (x: number, y: number) => number { console.log(f(1, 1)); console.log("foo"); return f; } let add: (x: number, y: number)=>number = (x, y) => x + y; export function functionReturnClosure() { console.log(foo(add)(2,3)); } ================================================ FILE: tests/samples/function_scope_var.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function useBeforeDeclare() { vp_i1 = 2; let vp_1 = vp_i1; // eslint-disable-next-line no-var var vp_i1 = 1; vp_1 = vp_i1; return vp_1; } // 1 export function operateWithConst() { vv2 = 3; var vv2: number; var vv2 = 8; const vv2_const = vv2; return vv2 + vv2_const; } // 16 export function nestedFunction() { vv3 = 5; { var vv3 = 9; } { var vv3 = 10; } function funcvv3_inner() { var vv3 = 15; } return vv3; } // 10 ================================================ FILE: tests/samples/generic_class.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class MObjectBase { id: number constructor() { this.id = 0; console.log("MObjectBase constructor: ", this.id); } } class MObject extends MObjectBase { name: T; constructor(name: T) { super(); this.name = name; console.log("MObject constructor: ", this.name); } action(say: T) { console.log(say); } } export function test_GenericClassWithSingleGenericType() { const cat = new MObject('cat'); cat.action('hello'); const robot = new MObject(12345); robot.action(54321); } class Generic { xxx: X; a: number; constructor(x: X) { this.xxx = x; this.a = 0; console.log("Generic constructor: ", this.xxx); } say(x: X) { return x; } } class GenericBase1 extends Generic { xx: X; yy: Y; constructor(x: X, y: Y) { super(y); this.xx = x; this.yy = y; console.log("GenericBase1 constructor: ", this.xx, this.yy); } } class GenericBase2 extends Generic { xx: X; constructor(x: X) { super(x); this.xx = x; console.log("GenericBase2 constructor: ", this.xx); } } class GenericClass1 extends GenericBase1 { private x: X; get value(): X { return this.x; } set value(x: X) { if (this.x == x) return; this.x = x; } constructor(x: X, y: Y, z: Z) { super(z, x); this.x = x; console.log("GenericClass1 constructor: ", x, y, z); } echo(param: X) { return param; } } class GenericClass2 extends GenericBase2 { private x: X; get value(): X { return this.x; } set value(x: X) { if (this.x == x) return; this.x = x; } constructor(x: X, y: Y) { super(y); this.x = x; console.log("GenericClass2 constructor: ", x, y); } echo(param: Y) { return param; } } export function test_GenericClassWithMultipleGenericTypes() { const GenericClass_string_number_boolean = new GenericClass1('hello', 1, true); GenericClass_string_number_boolean.value = 'world'; console.log(GenericClass_string_number_boolean.value); console.log(GenericClass_string_number_boolean.echo("123")); const GenericClass_number_number_number = new GenericClass1(1, 2, 3); GenericClass_number_number_number.value = 11 console.log(GenericClass_number_number_number.value); console.log(GenericClass_number_number_number.echo(111)); } export function test_GenericClassWithSameBase() { const GenericClass_number_string = new GenericClass2(1, 'hello'); GenericClass_number_string.value = 2; console.log(GenericClass_number_string.value); console.log(GenericClass_number_string.echo("world")); const GenericClass_string_number = new GenericClass2('world', 11); GenericClass_string_number.value = 'hello'; console.log(GenericClass_string_number.value); console.log(GenericClass_string_number.echo(111)); } interface I { getT(): T; } interface IGeneric extends I { iGet(): T; iSet(t: T): void; echo(v: V): V; } class CGeneric implements IGeneric { private t: T; private v: V; get value(): T { return this.t; } set value(t: T) { if (this.t == t) return; this.t = t; } constructor(t: T, v:V) { this.t = t; this.v = v; } getT(): T { return this.t; } iGet(): V { return this.v; } iSet(v: V): void { this.v = v; } echo(t: T): T { return t; } } export function test_GenericClassWithImplementsInfc() { const generic_class = new CGeneric('hello', 1); console.log(generic_class.value); generic_class.value = 'world'; console.log(generic_class.value); console.log(generic_class.getT()); generic_class.iSet(2); console.log(generic_class.iGet()); console.log(generic_class.echo('111')); } type MyObject = { a: T; b: U; } export function test_GenericClassWithTypeAlias() { const obj1: MyObject = {a: 'John', b: 18}; console.log(obj1.a); console.log(obj1.b); const obj2: MyObject = {a: 123, b: true}; console.log(obj2.a); console.log(obj2.b); } interface II { get(info: T) : T; } class A implements II { get(info: T) { return info; } } function get(obj: any) { return (obj as II).get(1); } export function test_AS() { const a = new A(); console.log(get(a)); } class M { foo(a: T) { console.log('M.foo: ' + a); } } class N extends M { x: number constructor(x: number) { super(); this.x = x; } foo(a: T) { console.log('N.foo: ' + a); } bar(a: T) { console.log('N.bar: ' + a); } } export function test_ClassWithGenericMethod() { let m: M = new M(); m.foo(1); m.foo('hello'); const n: N = new N(1); n.foo(1); n.foo('hello'); n.bar(2); n.bar('world'); m.foo(false); m = new N(2); m.foo(true); } class Foo { foo(data: T[]) { const a = data[0]; console.log(a); } } class Bar { bar(a: Foo, data: T[]) { a.foo(data); } } export function test_GenericMethodCall() { const foo = new Foo(); const bar = new Bar(); bar.bar(foo, [2, 3]); bar.bar(foo, ['hello', 'world']); } ================================================ FILE: tests/samples/generic_func.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ function genericFunc(param: T): T { return param; } function genericArrayFunc(param: T): T[] { return [param]; } export function numberFunc() { console.log(genericFunc(100)); } export function booleanFunc() { console.log(genericFunc(true)); } export function stringFunc() { console.log(genericFunc('hello')); } export function stringArrayFunc() { console.log(genericArrayFunc('hi')[0]); } ================================================ FILE: tests/samples/generic_param.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ function anyFunc(num: any) { console.log(num); } function genericFunc(num: T) { anyFunc(num); } type ForCallback = (item: T, index: number) => number export function testGenericParam() { genericFunc('hi'); const fn: ForCallback = (item: number, index: number): number => {return item + index} anyFunc(fn(1, 2)); } type ItemGenerator = (item: T, index?: U) => void function func1(func: ItemGenerator, a: T, b: U) { func(a, b); } function func2(func: ItemGenerator, a: T, b: U) { func(b, a); } export function typeFunc() { const itemGenerator1: ItemGenerator = (item: boolean, index?: number) => {console.log(item)}; func1(itemGenerator1, true, 444); const itemGenerator2: ItemGenerator = (item: string, index?: boolean) => {console.log(index)}; func2(itemGenerator2, false, 'hello'); } ================================================ FILE: tests/samples/global_generics_function.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ function global_func(value: T) { console.log("call global function"); return value; } export function test() { let number_tmp = global_func(2023); console.log(number_tmp); let string_tmp = global_func("hello world"); console.log(string_tmp); } ================================================ FILE: tests/samples/global_value.ts ================================================ /* eslint-disable prefer-const */ /* eslint-disable @typescript-eslint/no-unused-vars */ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class Test { static getNum(): number { return 0; } getNum1(): number { return 1; } } interface F { getNum1(): number; } let t: Test; t = new Test(); if (t.getNum1()) { console.log(1); } if (Test.getNum()) { console.log(0); } const num = 10 + 10; let num1: number; num1 = num; const bool_ = t.getNum1() ? true : false; let bool_1: boolean; bool_1 = bool_; function helper() { return 'hello'; } const str = helper(); let str1: string; str1 = str; function helper1() { return new Array(); } const arr = helper1(); let arr1: Array; arr1 = arr; let any1: any; any1 = 10; const any2 = undefined; let any3: any; any3 = any2; const f: F = t; console.log(f.getNum1()); export function entry() { // entry } ================================================ FILE: tests/samples/global_variables.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ const globalNumber = 99; const globalObject = { a: 1, b: true } const globalAny: any = 100; export function globalVarTest1() { let globalVariable_case1_2: number; // eslint-disable-next-line @typescript-eslint/no-unused-vars, prefer-const globalVariable_case1_2 = globalNumber; return globalNumber + globalVariable_case1_2 + globalObject.a + globalAny; } export function globalVarTest2() { // eslint-disable-next-line @typescript-eslint/no-unused-vars, prefer-const let globalVariable_case2_2: number; globalVariable_case2_2 = globalNumber; return globalNumber == globalVariable_case2_2; } const globalVar1 = 1; const globalVar2 = 2 + 3; const globalVar3 = true; const globalVar4 = 'hello'; const globalVar5: any = 1; let globalVar6 = 6; let globalVar7 = [1, 2]; let globalVar8: any = [1, 2]; export function globalVarTest3() { return globalVar1 + globalVar2 + globalVar6 + globalVar7[1]; } // 14 ================================================ FILE: tests/samples/if_statement_case1.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function ifElse() { const a = 10; const b = 5; const c = 1; let d = 1; if (a > 1) { d = 10; } else { d = 100; } return a + b + c + d; } // 26 export function nestedIf() { let a = 10; const b = 5; const c = 1; let d = 1; if (a > 1) { d = 10; if (b > 2) { a = d + 10; } } else { d = 100; } return a + b + c + d; } // 36 export function noElseBranch() { const a = 10; const b = 5; const c = 1; const d = 1; // eslint-disable-next-line no-empty if (a > 1) { } return a + b + c + d; } // 17 class A { // } export function returnInIf(x: number) { const res: A | null = null; if (!res) { if (x) { return x; } } return -1; } export function stringAsCond() { let b = ''; if (!b) { console.log('b'); } b = 'b'; if (b) { console.log(b); } } function noBlockInIfStmt(params?: string) { if (params) return console.log('params is not undefined'); else console.log('else when not undefined'); if (!params) return console.log('params is undefined'); else return console.log('else when undefined'); } export function paramNotUndefined() { noBlockInIfStmt('a'); } export function paramUndefined() { noBlockInIfStmt(); } ================================================ FILE: tests/samples/ignore_parameter_in_variable.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class VNode { id: number type: string constructor(id: number, type: string) { this.id = id; this.type = type; } } class Component { nodeMap__: { [key: string]: VNode } = { name: { id: 1024, type: "wasm" } } } export function test_ignore_parameter() { let node: Component = new Component(); console.log(node.nodeMap__.name.id); // 1024 console.log(node.nodeMap__.name.type); // wasm } ================================================ FILE: tests/samples/import_alias_identifier.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { exportFunc, exportVar, ExportClass, exportNS, } from './export_alias_identifier'; export function getAliasFunc() { return exportFunc(); } export function getAliasVar() { return exportVar; } export function getAliasClass() { return new ExportClass().getField(); } export function getAliasNS() { return exportNS.aFunc(); } ================================================ FILE: tests/samples/import_alias_reexport_identifier.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { oriExportFunc as exportFunc, oriExportVar as exportVar, OriExportClass as ExportClass, oriExportNS as exportNS, } from './export_alias_imported_identifier'; export function getAliasFunc() { return exportFunc(); } export function getAliasVar() { return exportVar; } export function getAliasClass() { return new ExportClass().getField(); } export function getAliasNS() { return exportNS.aFunc(); } ================================================ FILE: tests/samples/import_alias_scope_identifier.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import * as T from './export_alias_identifier'; export function getAliasFunc() { return T.exportFunc(); } export function getAliasVar() { return T.exportVar; } export function getAliasClass() { return new T.ExportClass().getField(); } export function getAliasNS() { return T.exportNS.aFunc(); } ================================================ FILE: tests/samples/import_class.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { ExportedClass } from './export_class'; class A extends ExportedClass{ } export function importClass() { const ec = new ExportedClass(); ec.a = 10; return ec.a; } import DefaultExportClass from './export_class'; let defaultExportClass = new DefaultExportClass(); class TestClass extends DefaultExportClass { public bar(x: number) { return x * 1; } } let testClass = new TestClass(); export function importClassAsBaseClass() { return TestClass.foo(1) + testClass.bar(2) + defaultExportClass.bar(2); } import { BaseI } from './export_class'; interface DerivedInfcI extends BaseI { y: string; } class DerivedClassI implements BaseI { y = "2"; x = 1; } export function importClassAsInterface() { let derivedClassI = new DerivedClassI(); const infc: DerivedInfcI = { x: 1, y: "2"}; return derivedClassI.x + infc.x; } ================================================ FILE: tests/samples/import_expression.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import objLiteral from './export_expression_obj_literal' import numberLiteral from './export_expression_number_literal' export function defaultObjLiteral() { const obj = objLiteral; return obj.a; } export function defaultNumberLiteral() { return numberLiteral; } ================================================ FILE: tests/samples/import_func.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import defaultFunction, { addFunc as add, sub, mulFunc as mul, divFunc as divFunc, } from './export_func'; import { exportedFunc } from './export_func_invoked'; import {testLog} from './export_from/libA/index' export function importFuncAdd() { const a = add(1, 2); return a; } export function importFuncSub() { const a = sub(1, 2); return a; } export function importFuncMul() { const a = mul(1, 2); return a; } export function importFuncDiv() { const a = divFunc(4, 2); return a; } export function importDefaultFunc() { return defaultFunction(); } export function importFuncInvoked() { const importedFunc = exportedFunc; return importedFunc(); } export function importFuncByExportFrom(){ testLog(); } ================================================ FILE: tests/samples/import_implicit_type.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { obj_array } from "./export_implicit_type" export function getObjTypeFromImport() { const obj = obj_array[0]; console.log(obj.idx); console.log(obj.newId); } ================================================ FILE: tests/samples/import_namespace.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import defaultNS, { namespaceA as nsA } from './export_namespace'; export function importNamespaceFunc() { const nsAResult = nsA.bFunc(); return nsAResult; } export function importNamespaceVar() { const nsVar = defaultNS.aVar; return nsVar; } ================================================ FILE: tests/samples/import_type.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { Pair, funcType, objLiteralType } from './export_type'; import defaultType, { globalType, exportTypeNS} from './export_re_export'; export function validatePrimitiveType() { const defaultValue: defaultType = 100; console.log(defaultValue); const globalTypeValue: globalType = 'hi'; console.log(globalTypeValue); const nsTypeValue: exportTypeNS.innerType = true; console.log(nsTypeValue); } export function validateObjType() { const obj: objLiteralType = { a: 'hello', b: 18, } console.log(obj.a); console.log(obj.b); } export function validateFuncType() { const fn: funcType = () => {return 19}; return fn(); } export function validateTypeArguments() { const objValue: Pair = { first: 10, second: false, } console.log(objValue.first); console.log(objValue.second); } ================================================ FILE: tests/samples/import_var.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import * as vars from './export_var'; export function importVarA() { return vars.aVar; } export function importVarB() { return vars.bVar; } export function importVarC() { return vars.c; } ================================================ FILE: tests/samples/infc_assign_class.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ interface I { x: number; y: boolean; } class Foo { y: boolean; x: number; constructor() { this.x = 1; this.y = false; } } export function classAndInfc() { const i: I = new Foo(); const f: Foo = i; return f.x; } interface I2 { y: string; x: number; z: () => number; set m(v: number); get m(); } class A implements I2 { x: number; y: string; _m: number; get m() { return this._m; } set m(v: number) { this._m = v; } constructor(xx: number, yy: string, mm: number) { this.x = xx; this.y = yy; this._m = mm; } z() { return this.m + this.x; } } export function infcImpl() { const i: I2 = new A(1, '2', 2); if (i.y === '2') { i.m = 10; return i.z() + i.m; } return 0; } interface I1 { x: number; y: A1; } class A1 { arr: I1[] = []; } /** when rec group contain infc, remove it from circle */ export function removeInfcFromRecGroup() { const a = new A1(); a.arr.push({ x: 1, y: new A1() }); return a.arr[0].x; } interface I3 { x: number; y?: string; } class C implements I3 { y: string; x: number; constructor() { this.x = 1; this.y = '2'; } } export function infcImplWithOptionalField() { const i: I3 = new C(); console.log(i.y); } interface I4 { x: number; y: number; } class A4 { x = 0; y = 1; } export function infcCastToObject() { const i4: I4 = {x: -1, y: -2}; const a: A4 = i4 as A4; console.log(a.x, a.y); } ================================================ FILE: tests/samples/infc_assign_infc.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ interface I { x: number; y: boolean; } interface I2 { y: boolean; x: number; z: string; } export function infcAndInfc() { const i: I2 = { y: true, x: 10, z: 'str' }; const f: I = i; return f.x; } ================================================ FILE: tests/samples/infc_assign_obj.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ interface I { x: number; y: boolean; } /** interface will a substitution field */ interface I1 { x: {}; } export function objLiteralAndInfc() { const i: I = { x: 1, y: false }; let o = { x: 10, y: true }; o = i; return o.x; } interface RouteInfo { package: string; path: string; value: number; } const route: RouteInfo = { path: '', package: '', value: 12, }; export function infcInitGlobal() { return route.value; } ================================================ FILE: tests/samples/infc_case18.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ interface I { x: number; y: boolean; set _x(x: number); get _x(); } class Foo { y: boolean; z: string; x: number; constructor() { this.x = 1; this.y = false; this.z = 'str'; } test() { return this.y; } set _x(x: number) { this.x = x; } get _x() { return this.x; } } export function infc18() { const f = new Foo(); const i: I = f; i._x = 10; return i._x; } ================================================ FILE: tests/samples/infc_field_assign.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ interface I { x: number; z: string; } interface I2 { y: boolean; x: number; z: string; } interface I3 { x: number; y: boolean; } export function fieldAssignToOther() { const i1: I2 = { x: 1, y: true, z: 'str' }; const i: I = i1; const b = i.x; return b; } export function otherAssignToField() { const i: I3 = { y: true, x: 10 }; const b = 20; i.x = b; return i.x; } ================================================ FILE: tests/samples/infc_method.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ interface I { x: number; y: boolean; get _x(): number; set _x(x: number); } class Foo { y: boolean; z: string; x: number; constructor() { this.x = 1; this.y = false; this.z = 'str'; } test() { return this.y; } set _x(x: number) { this.x = x; } get _x() { return this.x; } } export function infcSetter() { const f = new Foo(); const i: I = f; i._x = 10; return i._x; } interface I2 { x: number; y: boolean; test: () => boolean; } export function infcMethod() { const f = new Foo(); const i: I2 = f; return i.test(); } export function infcGetter() { const f = new Foo(); const i: I = f; const m = i._x; return m; } interface I3 { x: (y: number) => (z: number) => number; y: () => Foo; } class Test { x(m: number) { return function (n: number) { return m + n; }; } y(){ return new Foo(); } } export function infcNestMethod() { const t = new Test(); const i: I3 = t; const f = i.y()._x + (i.y().test() ? 1 : 2) + i.x(1)(2); return f; //6 } interface I5 { func1: () => boolean; func2: () => number; } class C5 { func1() { return false; } func2() { return 1; } } export function infcMethodWithAnyInst() { const i: I5 = new C5; const a: any = i; return (a as I5).func2(); } type FuncType = () => void export interface I6 { callback: FuncType } export function ObjectLiteralFunctionFieldToInfc(){ let obj: I6 = { callback: () => { console.log('Hello') }, } let b = obj.callback b(); } class C6 { callback: FuncType constructor(cb: FuncType){ this.callback = cb; } } export function ClassFunctionFieldToInfc(){ let obj: I6 = new C6(() => { console.log('Hello') }); let b = obj.callback b(); } ================================================ FILE: tests/samples/infc_parameter.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ interface I { x: number; y: boolean; } interface I2 { y: boolean; x: number; z: string; } class Foo { y: boolean; z: string; x: number; constructor() { this.x = 1; this.y = false; this.z = 'str'; } } function testInfc(f: Foo) { return f; } export function infcToClass() { const i: I2 = { x: 1, y: true, z: 'str' }; const f = testInfc(i); return f.x; } export function classToInfc() { const i: Foo = new Foo(); const f = testInfc(i); return i.x + f.x; } function foo(i: I2) { return i.y; } function funcAsParameter(i: (i: I2) => boolean) { const f: Foo = new Foo(); const res = i(f); return res; } export function infcAsParameter() { return funcAsParameter(foo); } ================================================ FILE: tests/samples/infc_return_value.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ interface I { x: number; y: boolean; } interface I2 { y: boolean; x: number; z: string; } class Foo { y: boolean; z: string; x: number; constructor() { this.x = 1; this.y = false; this.z = 'str'; } } function foo(): I { const i = { y: true, x: 10, z: 'str' }; return i; } function foo2(): Foo { const i: I2 = { y: true, x: 10, z: 'str' }; return i; } export function returnInfc() { const infc = foo(); return infc.y; } export function returnClass() { const obj = foo2(); return obj.x; } ================================================ FILE: tests/samples/infc_with_array.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ interface I { x: number; y: boolean; } export function infcWithArray() { const i = new Array(2); i[0] = { y: true, x: 12 }; return i[0].x; } ================================================ FILE: tests/samples/inner_generics_function.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function test() { function inner_func(value: T) { console.log("call inner function"); return value; } let number_tmp = inner_func(2023); console.log(number_tmp); let string_tmp = inner_func("hello world"); console.log(string_tmp); } ================================================ FILE: tests/samples/instanceof.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A { constructor() { // } a() { console.log('a'); } } class B extends A { b() { console.log('b'); } } class C extends B { c() { console.log('c'); } } class D { a() { // } b() { // } } // suitable for B/C interface I { a(): void; b(): void; } export function instanceofTest() { // no need to call native API const i: C = new C(); console.log(i instanceof B); const j = new D(); console.log(j instanceof D); console.log(i instanceof D); // need to call native API let k: I = new B(); console.log(k instanceof A); k = new D(); console.log(k instanceof A); let l: any = new B(); console.log(l instanceof A); l = 1; console.log(l instanceof A); l = k; console.log(l instanceof A); k = new B(); l = k; if (l instanceof A) { const m = l as A; m.a(); } let n: A | D = new C(); if (n instanceof B) { const o = n as B; o.b(); n = new D(); } if (n instanceof B) { console.log('n is B'); } const func = () => { return k; }; console.log(func instanceof Function); console.log(func instanceof Object); console.log(func instanceof B); } class Base { } class Base_1 extends Base { } class Base_1_1 extends Base_1 { } function leftBaseRightSuperInner(left: Base) { if (left instanceof Base_1) { console.log('is Base_1') }else{ console.log('is not Base_1') } } export function leftBaseRightSuper(){ let aa = new Base_1_1(); leftBaseRightSuperInner(aa); } ================================================ FILE: tests/samples/lib.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function test() { console.log('hello') } ================================================ FILE: tests/samples/loop_do_while.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function loopBodyEmpty() { const o = 9; const c = 10; // eslint-disable-next-line no-empty do {} while (c > 100); return c; } export function basicDoLoop() { let c = 10; do { c++; } while (c < 100); return c; } export function prefixPlusPlus(): number { let o = 9; let c = 10; do { c++; } while (++o < 20); return c; } export function suffixPlusPlus(): number { let o = 9; let c = 10; do { c++; } while (o++ < 20); return c; } export function numberAsCondition(): number { let o = 9; let c = 10; do { c--; } while (c); return c; } export function doWhileWithContinue() { const expect = [1, 3, 5, 3, 4, 5, 6, 7, 1, 3, 1, 3, 5, 6, 7, 9]; const res: number[] = []; let i = 0; do { i++; if (i % 2 === 0) continue; res.push(i); } while (i < 5); i = 0; do { i++; if (i < 3) continue; if (i > 7) continue; res.push(i); } while (i < 10); i = 0; do { i++; if (i === 5) break; if (i % 2 === 0) continue; res.push(i); } while (i < 10); i = 0; do { i++; if (i % 2 === 0 && i % 3 !== 0) continue; res.push(i); } while (i < 10); if (res.length !== expect.length) { return false; } for (let i = 0; i < expect.length; i++) { if (res[i] !== expect[i]) { return false; } } return true; } ================================================ FILE: tests/samples/loop_for.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function basicCase(): number { let c = 100; for (let q = 10; q > 4; --q) { c = c + 2; c--; } return c; } export function loopBodySemicolon(): number { const c = 100; for (let i = 2; i < 5; i++); return c; } export function loopBodyEmpty(): number { const c = 100; // eslint-disable-next-line no-empty for (let k = 10; k > 4; --k) {} return c; } export function noInitializer(): number { let c = 100; let j = 0; for (; j < 10; ++j) { c--; } return c; } export function noCondition(): number { let c = 100; let j = 0; for (j = 0; ; ++j) { c--; if (j > 10) { break; } } return c; } export function noIncrement(): number { let c = 100; let j = 0; for (j = 0; j < 10;) { c--; j++; } return c; } export function nestedForLoopWithBreak(): number { let c = 100; for (let i = 1; i < 10; i++) { c++; for (let j = 1; j < 5; j++) { c++; if (c > 108) { break; } } break; } return c; } export function multipleForLoop(): number { let c = 100; for (let q = 10; q > 4; --q) { c = c + 2; } for (let i = 0; i < 3; i++) { c = c + 1; } return c; } export function loopWithCommaToken() { let sum = 0; let str = ''; for (let i = 0, j = 4; i < 10; i++, j += 2) { sum += i; sum += j; } sum++, str = '123', --sum; return sum + (str == '123' ? 1 : 2); // 176 } export function loopWithContinue() { const expect = [0, 2, 0, 2, 0, 0, 2, 2, 0, 2, 0, 0, 2, 0, 2, 2, 1, 3, 5, 1, 3]; const res: number[] = []; for (let i = 0; i < 3; i++) { if (i === 1) { continue; } res.push(i); } for (let i = 0; i < 3; i++) { if (i === 1) { continue; } for (let j = 0; j < 3; j++) { if (j === 1) { continue; } res.push(j); } res.push(i); } for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { if (j === 1) { continue; } res.push(j); } if (i === 1) { continue; } res.push(i); } for (let i = 0; i < 10; i++) { if (i % 2 === 0) continue; if (i > 5) continue; res.push(i); } for (let i = 0; i < 10; i++) { if (i === 5) break; if (i % 2 === 0) continue; res.push(i); } if (res.length !== expect.length) { return false; } for (let i = 0; i < expect.length; i++) { if (res[i] !== expect[i]) { return false; } } return true; } ================================================ FILE: tests/samples/loop_while.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function basicLoop(): number { let c = 100; while (c > 90) { c = 10; } return c; } export function loopBodyEmpty(): number { const c = 100; // eslint-disable-next-line no-empty while (c > 100) {} return c; } export function loopBodySemicolon(): number { const c = 100; // eslint-disable-next-line no-empty while (c > 100); return c; } export function complexLoop(): number { let c = 100; while (c > 0) { c--; for (let i = 0; i < 100; i++) { c--; if (c < 50) { break; } } break; } return c; } export function whileWithContinue(): boolean { const expect = [3, 4, 5, 6, 7, 1, 3]; const res: number[] = []; let i = 0; while (i < 10) { i++; if (i < 3) continue; if (i > 7) continue; res.push(i); } i = 0; while (i < 10) { i++; if (i % 2 === 0) continue; if (i === 5) break; res.push(i); } if (res.length !== expect.length) return false; for (let i = 0; i < expect.length; i++) { if (res[i] !== expect[i]) return false; } return true; } ================================================ FILE: tests/samples/map_callback.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ function logMapElements(value: any, key: any, map: any) { console.log(key, value, map); } export function map_forEach() { const mmap: any = new Map(); mmap.set('0', 3); mmap.set('1', 4); mmap.set('2', 5); mmap.forEach(logMapElements); /* 0 3 [object Map] 1 4 [object Map] 2 5 [object Map] */ } export function map_get() { const mmap: any = new Map(); mmap.set('0', 'Tom'); mmap.set('1', 'Jack'); mmap.set('2', 'Bob'); console.log(mmap.get('2')); // Bob } ================================================ FILE: tests/samples/mixed_type.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A { x: any = -1; y = false; z = 222; i = '123'; } export function mixTypeInClass() { const a = new A(); let b: any = a; b.x = '1'; b.y = true; b.z = 0; b.i = '0'; b.j = '00'; console.log(b.x); console.log(b.y); console.log(b.z); console.log(b.i); console.log(b.j); const c = a as A; console.log(c.x); console.log(c.y); console.log(c.z); console.log(c.i); } interface I { x: any; i: string; z: number; y: boolean; } export function mixTypeInInfc() { const a: I = new A(); let b: any = a; b.x = '2'; b.y = true; b.z = -1; b.i = '-1'; b.j = '--1'; console.log(b.x); console.log(b.y); console.log(b.z); console.log(b.i); console.log(b.j); const c = a as I; console.log(c.x); console.log(c.y); console.log(c.z); console.log(c.i); } ================================================ FILE: tests/samples/module-case/export_declare.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export declare function declareAdd(x: number, y: number): number; export declare const declareVar: number; ================================================ FILE: tests/samples/module-case/export_normal.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function add(a: number, b: number): number { return a + b; } ================================================ FILE: tests/samples/module-case/import_case1.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { add as renameAdd } from './export_normal'; import { add } from '../export_func'; import { declareAdd, declareVar } from './export_declare'; const invokeImportedRenameFuncRes = renameAdd(1, 2); const invokeImportedFuncRes = add(1, 2); const invokeDeclareFuncRes = declareAdd(5, 6); const importedDeclaredVar = declareVar; const importedDeclaredFunc = declareAdd; importedDeclaredFunc(7, 8); ================================================ FILE: tests/samples/module_start_A.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { C_var } from './module_start_C'; import { B_var } from './module_start_B'; export let A_var = 1000; A_var = A_var + B_var + C_var; export function getAVar() { return A_var; } export function getBVar() { return B_var; } export function getCVar() { return C_var; } ================================================ FILE: tests/samples/module_start_B.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { C_var } from './module_start_C'; export let B_var = 100; B_var = B_var + C_var; export function getBVar() { return B_var; } export function getCVar() { return C_var; } ================================================ FILE: tests/samples/module_start_C.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export let C_var = 10; C_var += 10; export function getCVar() { return C_var; } ================================================ FILE: tests/samples/namespace_func.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ namespace NSCaseFunc { export function case2() { return 2; } case2(); } export function namespaceFunc() { const nsFunc = NSCaseFunc.case2; return nsFunc(); } ================================================ FILE: tests/samples/namespace_generics_function.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ namespace ceil { export namespace ceil1 { export function namespace_func(value: T) { console.log("call namespace function"); return value; } } } export function test() { let number_tmp = ceil.ceil1.namespace_func(2023); console.log(number_tmp); let string_tmp = ceil.ceil1.namespace_func("hello world"); console.log(string_tmp); } ================================================ FILE: tests/samples/namespace_nest.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ let nscase3_global1 = 1; namespace NSCaseOutter { namespace NSInner { function case2() { nscase3_global1 *= 5; } case2(); } function case2() { nscase3_global1 += 1; } case2(); } nscase3_global1 += 2; namespace NSHelper { function case3() { nscase3_global1 -= 10; } case3(); } export function namespaceNested() { nscase3_global1 += 1; return nscase3_global1; } // -1 ================================================ FILE: tests/samples/namespace_var.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ namespace NSCaseVar { export const a = 1; export const b = true; } export function namespaceVar() { const nsA = NSCaseVar.a; return nsA; } ================================================ FILE: tests/samples/nest_generic.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ // case 1: // Test when the type of typealias is generic type ItemGenerator = (item: T, index?: U) => void function func1(func: ItemGenerator, a: T, b: U) { func(b, a); } export function test1() { const itemGenerator1: ItemGenerator = (index: number, item?: boolean) => {console.log(index); console.log(item)}; func1(itemGenerator1, true, 1); const itemGenerator2: ItemGenerator = (item: string, index?: number) => {console.log(index); console.log(item)}; func1(itemGenerator2, 2, 'hello'); } // case 2 // Test when function parameters are generic class Foo { foo(data: T[]) { const a = data[0]; console.log(a); } } class Bar { bar(a: Foo, data: T[]) { a.foo(data); } } class Foo1 { _x: T; constructor(x: T) { this._x = x; } set x(x: T) { this._x = x; } get x() { return this._x; } foo(data: T[]) { console.log(this._x); console.log(data[0]); } } class Bar1 { _y: U; constructor(y: U) { this._y = y; } bar(a: Foo1, data: U[]) { console.log(this._y); a.foo(data); } } function func2(a: Foo, b: Bar, data: U[]) { b.bar(a, data); } function func3(a: Foo1, b: Bar1, data: U[]) { b.bar(a, data); } export function test2() { const foo = new Foo(); const bar = new Bar(); bar.bar(foo, [1, 2]); bar.bar(foo, ['hello', 'world']); } export function test3() { const foo1 = new Foo1(1); foo1.x = 3; console.log(foo1.x); const bar1 = new Bar1(2); bar1.bar(foo1, [1, 2]); const foo2 = new Foo1('11'); foo2.x = '33'; console.log(foo2.x); const bar2 = new Bar1('22'); bar2.bar(foo2, ['hello', 'world']); } export function test4() { const a = new Foo(); const b = new Bar(); func2(a, b, [1, 2]) func2(a, b, ['hello', 'world']); const c = new Foo1(1); const d = new Bar1(2); const e = new Foo1('hello'); const f = new Bar1('world'); func3(c, d, [3, 4]); func3(e, f, ['111', '222']); } // case 3 // Test when generic functions are nested function func5(a: X, b: Y) { const foo = new Foo1(a); const bar = new Bar1(b); console.log(foo.x); console.log(bar._y); } function func6(a: U, b: T) { func5(a, b); } export function test5() { func6(1, 'hello'); func6(false, 2); } // case 4 type funcType = (key: string, value: V) => void type AttrValue = undefined | string | number | boolean function echo(key: string, value: string) { console.log(key, value); } class ArrayMap2 { readonly keys: string[] = []; readonly values: V[] = []; get size() { return this.keys.length; } get(key: string): V | undefined { const idx = this.keys.indexOf(key) if (idx !== -1) { return this.values[idx]; } return undefined; } set(key: string, value: V) { const idx = this.keys.indexOf(key); if (idx !== -1) { this.values[idx] = value; } else { this.keys.push(key); this.values.push(value); } } delete(key: string) { const idx = this.keys.indexOf(key); if (idx !== -1) { this.keys.splice(idx, 1); this.values.splice(idx, 1); } } clear() { this.keys.splice(0, this.keys.length); this.values.splice(0, this.values.length); } forEach2(fn: funcType) { this.keys.forEach((key: string, index: number) => { fn(key, this.values[index]); }) } } export function test6() { const a = new ArrayMap2(); a.set('n1', 1); a.set('n2', 2); a.set('n3', 3); console.log(a.get('n2')); a.delete('n1'); console.log(a.size); const b = new ArrayMap2(); b.set('s1', '11'); b.set('s2', '22'); b.set('s3', '33'); b.forEach2(echo); console.log(b.get('s3')); b.clear(); console.log(b.size); const c = new ArrayMap2(); c.set('u1', '11'); c.set('u2', 22); c.set('u3', true); console.log(c.get('u1')); console.log(c.get('u2')); console.log(c.get('u3')); c.clear(); console.log(c.size); } ================================================ FILE: tests/samples/non_null_expression.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ /* Write some test cases to cover TypeScript non-null expression */ type funcT = (x: number) => number; export function test_non_null_func() { let fn : funcT | null; fn = (x: number) => { return x * 2; } return fn!(2); // 4 } export function test_non_null_field() { let obj = { a: 1, b: 2, c: { d: 3, e: { f: 4 } } } return obj!.c!.e!.f; // 4 } export function test_non_null_any() { let obj: any = { a: 1, b: 2, c: { d: 3, e: { f: 4 } } } console.log(obj!.c!.e!.f); // 4 console.log(obj!.x!); // undefined } ================================================ FILE: tests/samples/null_type_case1.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A { x = 10; static foo() { return 10; } } function test1() { const c: number | null = null; return c as null; } function test2() { const c: number | null = 10; return c as number; } function test3() { const a: A | null = new A(); return a.x; } function test4() { const a: A | null | null = null; return a; } export function nullTypeTest() { let temp = test1(); const res1 = test2(); const res2 = test3(); temp = test4(); return res1 + res2; } ================================================ FILE: tests/samples/obj_literal.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function simpleObject() { const obj1 = { a: 1, b: true, c: 'hi', }; return obj1.a; } // 1 export function nestedObject() { const obj1 = { a: 1, b: true, c: { d: 4, }, }; return obj1.c.d; } // 4 export function moreNestedObject() { const obj1 = { a: 1, b: true, c: { d: 4, e: { f: false, }, }, }; return obj1.c.e.f; } export function assignObjectLiteralToField() { const obj1 = { a: 1, b: true, c: { d: 4, }, }; obj1.c = { d: 6, }; return obj1.c.d; } // 6 export function withMethodField() { const i = (m: number) => { return m * m; }; const obj = { y: 11, x: i, z: { k: false, j: (x: number, y: number) => { return x + y; }, }, }; return obj.z.j(8, 9) + obj.x(10); } // 117 class A { x = 'xxx'; } class B extends A { y = 1; } export function structWithSameLayout() { const val = new A(); const res = { xx: val.x, y: 1, }; return res.y; } interface IA { name: string; say(n: number): number; say2(): void; } export function useThisInLiteralObj() { const a: IA = { name: "A", say(n: number) { console.log(this.name); return n; }, say2() { console.log(this.name); } } console.log(a.say(1)); a.say2(); const b = { name: "B", say(n: number) { console.log(this.name); return n; }, say2() { console.log(this.name); } } console.log(b.say(1)); b.say2(); const a2 = { name: "a2", say(n: number) { console.log(this.name); return n; }, say2: function() { console.log(this.name); } } console.log(a2.say(1)); a2.say2(); } interface Node { x?: number; y?: string; z?: boolean; } export function infcInitWithLiteralObj_completion() { const literal1 = {x: 10}; const node1: Node = {x: 10}; console.log(node1.x); console.log(node1.y); console.log(node1.z); const literal2 = {z: false}; const node2: Node = {z: false}; console.log(node2.x); console.log(node2.y); console.log(node2.z); } export function infcInitWithLiteralObj_reorder() { const literal = {z: false, x: 10, y: 'hello'}; const node: Node = {z: false, x: 10, y: 'hello'}; console.log(node.x); console.log(node.y); console.log(node.z); } interface I { x?: string y: any z: string[] } class Bar { i: I; constructor() { this.i = { x: undefined, y: undefined, z: [] } } } export function assignClassFieldWithObjectLiteral() { const bar = new Bar(); bar.i.z = ['hello']; console.log(bar.i.z[0]); } export function assignInfcFieldWithObjectLiteral() { const i: I = { y: undefined, z: [] }; i.z = ['world']; console.log(i.z[0]); } ================================================ FILE: tests/samples/obj_method_call.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ interface I { f1: () => void; f2: () => void; } class A { f1() { console.log('METHOD'); } f2 = () => { console.log('FIELD'); }; } export function callClassTypedFunc() { const a = new A(); a.f1(); a.f2(); } export function callClassTypedClosure() { const a = new A(); const f1 = a.f1; const f2 = a.f2; f1(); f2(); } export function callInfcTypedFunc() { const a: I = new A(); a.f1(); a.f2(); } export function callInfcTypedClosure() { const a: I = new A(); const f1 = a.f1; const f2 = a.f2; f1(); f2(); } ================================================ FILE: tests/samples/obj_property_access.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ interface I { [key: string]: number; } interface I_FUNC { [key: string]: () => number; } export function infc_obj_get_field() { const obj: I = { x: 1, y: 2, }; console.log(obj['x']); } export function infc_obj_set_field() { const obj: I = { x: 1, y: 2, }; obj['x'] = 100; console.log(obj['x']); } export function obj_get_field() { const obj = { x: 1, y: 2, }; console.log(obj['x']); } export function obj_set_field() { const obj = { x: 1, y: 2, }; obj['x'] = 100; console.log(obj['x']); } export function obj_get_method() { const obj = { x: () => 1, y: () => 2, }; const a = obj['x']; console.log(a()); } export interface I1 { [key: string]: any; } export type T1 = (params?: I1) => void; export interface I2 { [key: string]: T1; } export function infc_obj_get_instance_method() { const obj: I2 = { a: (params?: I1) => { console.log('hi'); }, }; const a = obj['a']; a(); } export function infc_obj_get_vtable_method() { const obj: I_FUNC = { x: () => 1, y: () => 2, hello() { return 5; } }; const a = obj['hello']; console.log(a()); } export function infc_obj_set_method() { const obj: I_FUNC = { x: () => 1, y: () => 2, }; obj['x'] = () => 100; const a = obj['x']; console.log(a()); } export function obj_set_method() { const obj = { x: () => 1, y: () => 2, }; obj['x'] = () => 100; const a = obj['x']; console.log(a()); } type FuncType = () => void; interface I_CB { callback: FuncType | null; } function getNullableFuncInner(cb: FuncType | null) { const obj: I_CB = { callback: cb, }; const cbFunc = obj.callback; if (cbFunc !== null) { console.log('cbFunc is not null'); cbFunc(); } else { console.log('cbFunc is null'); } } export function getNullableFunc() { const cb1: FuncType | null = null; getNullableFuncInner(cb1); const cb2: FuncType | null = () => { console.log('run cb'); }; getNullableFuncInner(cb2); } ================================================ FILE: tests/samples/obj_property_dynamic_access.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class PropClass { field: number = 100; } interface I1 { a: number; foo: ()=>number; prop: PropClass; } interface I2 { a: any; foo: any; prop: any; } export function dynamicGetUnboxingInObjLiteral() { const x: any = 1; const y: any = ()=>1; const z: any = new PropClass(); const obj: I1 = { a: x, foo: y, prop: z, }; const a = obj.a; console.log(a); const foo_invoke = obj.foo(); console.log(foo_invoke); const prop_field = obj.prop.field; console.log(prop_field); } export function dynamicGetBoxingInObjLiteral() { const obj: I2 = { a: 8, foo: ()=>8, prop: new PropClass(), }; const a = obj.a; console.log(a); const foo = obj.foo; const foo_invoke = foo(); console.log(foo_invoke); const prop_field = obj.prop.field; console.log(prop_field); } class A1 { a: any = 10; foo: any = ()=>1; prop: any = new PropClass(); } class A2 { a: number = 10; foo = ()=>1; prop = new PropClass(); } export function dynamicGetUnboxingInClass() { const obj: I1 = new A1(); const a = obj.a; console.log(a); const foo = obj.foo; const foo_invoke = foo(); console.log(foo_invoke); const prop_field = obj.prop.field; console.log(prop_field); } export function dynamicGetBoxingInClass() { const obj: I2 = new A2(); const a = obj.a; console.log(a); const foo = obj.foo; const foo_invoke = foo(); console.log(foo_invoke); const prop_field = obj.prop.field; console.log(prop_field); } export function dynamicSetUnboxingInClass() { const obj: I1 = new A1(); let value: any = 5; obj.a = value; console.log(obj.a); // The following case not compile success yet, since we set vtable immutable // value = ()=>5; // obj.foo = value; // const foo = obj.foo; // const foo_invoke = foo(); // console.log(foo_invoke); value = new PropClass(); obj.prop = value; const prop_field = obj.prop.field; console.log(prop_field); } export function dynamicSetBoxingInClass() { const obj: I2 = new A2(); const value1 = 5; const a = obj.a; console.log(a); // The following case not compile success yet, since we set vtable immutable // const foo = obj.foo; // const foo_invoke = foo(); // console.log(foo_invoke); const value2 = new PropClass(); const prop_field = obj.prop.field; console.log(prop_field); } interface I3 { x?: number | string; } class A3 { x: number | string | undefined = 6; } export function dynamicAccessInUnionType() { const obj: I3 = new A3(); const value2 = 11; obj.x = value2; const bbb = obj.x; console.log(bbb); } ================================================ FILE: tests/samples/op_ref_type.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function judgeIsRefNull() { const accounts = [{a: 1}, {a: 2}]; const account1 = accounts[0]; const account2 = accounts[1]; const account3 = null; if (account1 && account2) { console.log('both find') } else { console.log('at least one found'); } if (account1 && account3) { console.log('both find') } else { console.log('at least one found'); } if (account1 || account3) { console.log('one find') } else { console.log('no found'); } } ================================================ FILE: tests/samples/optional_method.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export type FuncType = () => void; export interface I1 { [key: string]: FuncType | undefined; } function innerFunc(obj: I1, funcName: string) { const fn = obj[funcName]; if (fn != undefined) { fn(); } else { console.log('fn is undefined'); } } export function optionalMethod() { const obj1: I1 = { foo: undefined, bar: () => { console.log('fn is bar'); }, }; innerFunc(obj1, 'foo'); innerFunc(obj1, 'bar'); const obj2: I1 = { x: undefined, }; innerFunc(obj2, 'foo'); innerFunc(obj2, 'bar'); } ================================================ FILE: tests/samples/optional_param.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ function optionalParam(a?: number) { return 10; } export function testOptionalParam() { return optionalParam(1) + optionalParam() + optionalParam(undefined); } function optionalParamAndRestParam(a: number, b?: string, ...rest: number[]) { return 10; } export function testOptionalParamAndRestParam() { return optionalParamAndRestParam(1) + optionalParamAndRestParam(1, 'hi') + optionalParamAndRestParam(1, 'hi', 2, 3, 4, 5); } class ContainOptionalMethodClass { optionalMethod(a?: any) { return 20; } partOptionalMethod(a: any, b?: number) { return 20; } partOptionalDefaultMethod(a: number, b: any = 8, c?: any) { return 20; } } const optionalMethodInst = new ContainOptionalMethodClass(); export function testOptionalMethod() { return optionalMethodInst.optionalMethod() + optionalMethodInst.optionalMethod(3); } export function testPartOptionalMethod() { return optionalMethodInst.partOptionalMethod('hi') + optionalMethodInst.partOptionalMethod('hi', 8); } /* TODO: now method don't sopport default parameter, so this case will not success */ /* export function testPartOptionalDefaultMethod() { return optionalMethodInst.partOptionalDefaultMethod(66) + optionalMethodInst.partOptionalDefaultMethod(66, 'hello') + optionalMethodInst.partOptionalDefaultMethod(66, 'hello', true); } */ ================================================ FILE: tests/samples/optional_property_access.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ interface I { x?: number; y?: string; } class B { num: number; i: I; constructor(ii: I, numm: number) { this.i = ii; this.num = numm; } } export function optionalField() { const b1 = new B({ x: 10, y: 'str' }, 10); const z = b1.i.y; console.log(z); b1.i.y = 'str1'; console.log(b1.i.y); const i: I = { x: 10 }; const b2 = new B(i, 11); console.log(b2.i.y); } interface I1 { x?: () => number; } class A1 { num: number; x() { return this.num; } constructor(num: number) { this.num = num; } } class A11 { // } export function optionalMethod() { const a = new A1(10); const i: I1 = a; let res1 = -1; if (i.x) { res1 = i.x(); } console.log(res1); let res2 = -1; const x = i.x; if (x) { // TODO: not support call closure of class method now // res2 = x(); res2 = 10; } console.log(res2); const res3 = i.x ? i.x() : -1; console.log(res3); let res4 = 0; const i11: I1 = new A11(); if (i11.x) { res4 += 10; } console.log(res4); } class A { x?: number; } export function classOptionalField() { const a = new A(); console.log(a.x); a.x = 10; console.log(a.x); } class B2 { str = 'hello'; } class A2 { b?: B2; } export function accessOptFieldOfOptField() { const a = new A2(); if (a.b) { console.log(a.b.str); } console.log(a.b?.str); a.b = new B2(); if (a.b) { console.log(a.b.str); } console.log(a.b?.str); } interface ITreeNode { left?: ITreeNode; right?: ITreeNode; } function checksum(node?: ITreeNode): number { if (!node) { return 1; } return 1 + checksum(node.left) + checksum(node.right); } export function accessOptionalUnionField() { const l: ITreeNode = {}; const r: ITreeNode = {}; const node: ITreeNode = {left: l, right: r}; const node1 = {left: l, right: r}; return checksum(node) + checksum(node1); } interface I2 { x?: number | boolean; } interface I3 { x: number | boolean; } class Foo { y = 10; } class Bar { y = 11; } interface I4 { x?: Foo | Bar; } interface I5 { x: Foo | Bar; } interface I6 { y?: I4; } interface I7 { y: I4 | I5; } interface I8 { y?: I4 | I5; } export function accessOptionalUnionField2() { let i2: I2 = { x: true }; console.log(i2.x); i2 = {}; console.log(i2.x); let i3: I3 = { x: true }; console.log(i3.x); i3 = { x: 10 }; console.log(i3.x); // It should not work, because `x` comes from {x: false} // i3.x = 10; // console.log(i3.x); const i4: I4 = { x: new Foo() }; if (i4.x) { console.log(i4.x.y); } i4.x = new Bar(); if (i4.x) { console.log(i4.x.y); } const I5: I5 = { x: new Bar() }; const x = I5.x; if (x instanceof Foo) { console.log(x.y); } else { console.log(x.y); } const i6: I6 = { y: { x: new Foo() } }; // console.log(); if (i6.y) { const x = i6.y.x; if (x instanceof Foo) { console.log(x.y); } } const i7: I7 = { y: { x: new Bar() } }; const obj = i7.y; const x1 = obj.x; if (x1) { if (x1 instanceof Foo) { console.log(x1.y); } else { console.log(x1.y); } } const i8: I8 = { y: { x: new Foo() } }; const obj1 = i8.y; let x11: Foo | Bar | undefined = undefined; if (obj1) { x11 = obj1.x; } if (x11) { if (x11 instanceof Foo) { console.log(x11.y); } else { console.log(x11.y); } } } type funcType = () => number; interface I_Optional_Func { y: number; x?: funcType; } export function accessOptionalFuncTypedField() { const i2: I_Optional_Func = { y: 1, x: undefined }; const a = i2.x; console.log(a); // The following case not compile success yet, since we set vtable immutable // i2.x = () => 8; // console.log(i2.x()); } ================================================ FILE: tests/samples/parenthesized_expression_case1.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function parenthesizedTest() { const a = 1; const b = 6; const c = b - a; const d = ((a + b) * c) / b; return d; } ================================================ FILE: tests/samples/percentToken.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function percentToken() { console.log(10 % 3); // 1 console.log(-10 % 3); // -1 console.log(-3 % 10); // -3 return -3 % 10; // -3:f64 } ================================================ FILE: tests/samples/primitiveType.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function constPrimitiveVars() { const priCase1Var1 = 3; const priCase1Var2 = 'hi'; const priCase1Var3 = true; const priCase1Var4 = false; const priCase1Var5 = null; if (priCase1Var3 && !priCase1Var4) { return priCase1Var1; } return -1; } // 3 export function letPrimitiveVars() { let priCase2Var1 = 3; let priCase2Var2 = 'hi'; let priCase2Var3 = true; let priCase2Var4 = false; let priCase2Var5 = null; let priCase2Var6 = priCase2Var1; const priCase2Var7 = priCase2Var1; if (priCase2Var3 && !priCase2Var4) { return priCase2Var6 + priCase2Var7; } return -1; } class A { // } export function nullundefinedCmp() { const g = new A(), h: any = g, i: any = g; const t1 = null, t2 = undefined; console.log(t1 === t2); // false console.log(t1 === null); // true console.log(t2 === undefined); // true console.log(t1 !== null); // false console.log(t2 !== undefined); // false console.log(g === undefined); // false console.log(g !== undefined); // true console.log(g === null); // false console.log(g !== null); // true console.log(10 === undefined); // false console.log(10 !== undefined); // true console.log(10 === null); // false console.log(10 !== null); // true const aa: A | null = null; console.log(aa === null); // true console.log(aa !== null); // false } export function NaNNumber() { const notANumber = NaN; if (notANumber) { console.log(0); } else { console.log(1); } if (!notANumber) { console.log(1); } else { console.log(0); } } ================================================ FILE: tests/samples/promise_chain.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function promiseChain() { Promise.resolve(123) .then( (res: number) => { console.log(res); return 456; }, (res: number) => { console.log(res); return 789; }, ) .then((res: number) => { console.log(res); return Promise.resolve('hello'); }) .then((res: string) => { console.log(res); return Promise.reject(false); }) .then((res: boolean) => { console.log(res); return Promise.resolve('hello'); }) .catch((error: any) => { console.log(error); }) .finally(() => { console.log('finally'); }); /* Output: 123 456 hello false finally */ } export function promiseMultiThen() { Promise.resolve(123) .then( (res: number) => { console.log(res); return res + 10; }, (res: number) => { console.log(res); return res + 100; }, ) .then( (res: any) => { console.log(res); return res + 10; }, (res: any) => { console.log(res); return res + 100; }, ) .then( (res: any) => { console.log(res); return res + 10; }, (res: any) => { console.log(res); return res + 100; }, ) .then( (res: any) => { console.log(res); return res + 10; }, (res: any) => { console.log(res); return res + 100; }, ); /* Output: 123 133 143 153 */ } ================================================ FILE: tests/samples/promise_constructor.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function newPromiseWithVoid() { const promiseInst: any = new Promise((resolve: any, reject: any) => { console.log('before call resolve') resolve(); console.log('after call resolve'); }); promiseInst .then((data: any) => { console.log('then_onFulfilled_func'); }); } export function newPromiseWithNumber() { const promiseInst: any = new Promise((resolve: any, reject: any) => { resolve(100); }); promiseInst .then((value: any) => { console.log(value); }); } export function newPromiseWithString() { const promiseInst: any = new Promise((resolve: any, reject: any) => { resolve('hello'); }); promiseInst .then((value: any) => { console.log(value); }); } ================================================ FILE: tests/samples/promise_immediate.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function immediateResolveWithNoArg() { const promiseInst: any = Promise.resolve(); promiseInst.then( (value: any) => { console.log('then_onFulfilled_func'); }, (reason: any) => { console.log('then_onRejected_func'); }, ); } export function immediateResolveWithArg() { const promiseInst: any = Promise.resolve(); promiseInst.then( (param: any) => { console.log('then_onFulfilled_func'); }, (param: any) => { console.log('then_onRejected_func'); }, ); } export function immediateReject() { Promise.reject().then( (value: any) => { console.log('then_onFulfilled_func'); }, (reason: any) => { console.log('then_onRejected_func'); }, ); } ================================================ FILE: tests/samples/promise_throw.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function promiseThrowError() { Promise.resolve(123) .then( (res: any) => { console.log(res); throw 456; }, (res: any) => { console.log(res); return 789; }, ) .then((res: any) => { console.log(res); return Promise.resolve('hello'); }) .then((res: any) => { console.log(res); throw false; }) .then((res: any) => { console.log(res); return Promise.resolve('hello'); }) .catch((error: any) => { console.log(error); }) .finally(() => { console.log('finally'); }); /* Output: 123 456 finally */ } function exception_catch_in_cb(res: any) { try { throw 'exception_catch_in_cb'; } catch (e) { console.log(res); } return 456; } export function promiseCatchInCB() { Promise.resolve(123) .then( exception_catch_in_cb, (res: any) => { console.log(res); return 789; }, ) .then((res: any) => { console.log(res); return Promise.resolve('hello'); }) .catch((error: any) => { console.log(error); }) .finally(() => { console.log('finally'); }); /* 123 456 finally */ } function exception_not_catch_in_cb(res: any) { try { throw 'exception_not_catch_in_cb'; } finally { console.log(res); } } export function promiseNotCatchInCB() { Promise.resolve(123) .then( exception_not_catch_in_cb, (res: any) => { console.log(res); return 789; }, ) .then((res: any) => { console.log(res); return Promise.resolve('hello'); }) .catch((error: any) => { console.log(error); }) .finally(() => { console.log('finally'); }); /* 123 exception_not_catch_in_cb finally */ } ================================================ FILE: tests/samples/prototype.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function setPrototype() { let prototypeObj: any = { height: 1, }; let obj: any = { weight: 2 }; obj.__proto__ = prototypeObj; return obj.height as number; } export function returnPrototypeObject() { let prototypeObj: any = { height: 1, }; let obj: any = { height: 2 }; obj.__proto__ = prototypeObj; return obj.__proto__; } ================================================ FILE: tests/samples/rec_types.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class Foo { id = 10; children: Array = []; addChild(child: Foo) { // to validate on V8, here does not use push // this.children.push(child); return child.id; } static x: Foo = new Foo(); child: Foo | undefined; } export function recursiveType1() { const f = new Foo(); // to validate on V8, here does not use push // f.children.push(new Foo()); f.children = new Array(1); f.children[0] = new Foo(); f.addChild(new Foo()); return f.children[0].id + f.id + Foo.x.id + f.addChild(new Foo()); } class Foo1 { id = 10; children: Array = []; addChild(child: Bar1) { // to validate on V8, here does not use push // this.children.push(child); return child.id; } child: Bar1 | undefined; } class Bar1 { id = 20; children: Array = []; addChild(child: Foo1) { // to validate on V8, here does not use push // this.children.push(child); return child.id; } child: Foo1 | undefined; } class Baz1 extends Bar1 { // } export function recursiveType2() { const bz = new Baz1(); bz.children = new Array(1); bz.children[0] = new Foo1(); bz.children[0].children = new Array(1); bz.children[0].children[0] = bz; const f = new Foo1(); return bz.children[0].children[0].id + bz.addChild(new Foo1()) + f.addChild(new Bar1()); } class T { constructor() { this.w = new A(); } w: A; } type FuncType = (a: T) => void; function defaultFunc(a: T) { console.log('use funcType in rec group'); } class A { test(a: FuncType) { const t = new T(); console.log('run class method'); a(t); } } export function defaultFuncUseRecType() { const a = new A(); a.test(defaultFunc); } ================================================ FILE: tests/samples/ref_type_cmp.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class C { x = 10; y = 11; test() { return 10; } } export function structCmpEq() { const c1 = new C(); const c2 = c1; const c3 = c1; return c2 == c3; } export function structCmpNotEq() { const c1 = new C(); const c2 = new C(); return c2 == c1; } export function arrayCmpEq() { const arr1 = new Array(); arr1.push('12'); const arr2 = arr1; const arr3 = arr1; return arr2 == arr3; } export function arrayCmpNotEq() { const arr1 = new Array(); const arr2 = new Array(); return arr2 == arr1; } interface I { x: number; y: number; test: () => number; } export function infcCmpEq() { const c = new C(); const i1: I = c; const i2: I = c; return i1 == i2; } export function infcCmpNotEq() { const c = new C(); const i1: I = new C(); const i2: I = c; return i1 == i2; } interface I2 { x: number; y: string; } export function infcClassCmpEq() { const obj = { y: '123', x: 123 }; const i: I2 = obj; return i === obj; } export function infcClassCmpNotEq() { const obj1 = { y: '123', x: 123 }; const i1: I2 = obj1; const obj2 = { y: '123', x: 123 }; const i2: I2 = obj2; return i1 === i2; } ================================================ FILE: tests/samples/rest_param_interface.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ interface I { x: number; test: () => string; } class C1 { x: number; y: string; test() { return this.y; } constructor() { this.x = 10; this.y = '123'; } } class C2 { y: boolean; x: number; test() { return 'test'; } constructor() { this.x = 10; this.y = false; } } export function restParameterTest() { function bar1(...b: I[]) { return b[0]; } const c1 = new C1(); const c2 = new C2(); return bar1(c1, c2); } ================================================ FILE: tests/samples/rest_param_number.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function restParameterTest() { function bar1(a: number, ...b: number[]) { const c = a + b[0] + b[1]; return c; } function bar2(a: number, ...b: number[]) { return a; } return bar1(10, 11, 12, 13) + bar2(14); } // 47 function test(a?: string) { if (a) { console.log(a); a = undefined; } else { console.log('undefined'); } } export function undefinedAsCond() { test('hello'); test(undefined); } class A { // } export function anyrefCond() { test('hello'); const a = new A(); const v: any = a; if (v) { console.log('v'); } const a1: A | null = null; const v1: any = a1; if (v1) { console.log('v1'); } let a2 = ''; const v2: any = a2; if (v2) { console.log('v2'); } a2 = 'hello'; const v3: any = a2; if (v3) { console.log('v3'); } const v4: any = ''; if (v4) { console.log('v4'); } const v5: any = 'hello'; if (v5) { console.log('v5'); } const v6: any = 0; if (v6) { console.log('v6'); } const v7: any = 1; if (v7) { console.log('v7'); } const v8: any = undefined; if (v8) { console.log('v8'); } const v9: any = false; if (v9) { console.log('v9'); } const v10: any = true; if (v10) { console.log('v10'); } const v11: any = {}; if (v11) { console.log('v11'); } const v12: any = { a: 11 }; if (v12) { console.log('v12'); } } function foo(...num: number[]) { let res = 0; for (let i = 0; i < num.length; i++) { res += num[i]; } return res; } export function restParamWithEmpty() { console.log(foo(1, 2)); console.log(foo()); } ================================================ FILE: tests/samples/return_statement.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function deadCodeAfterReturn(a: number, b: number) { if (a > b) { return a - b; a += 1; } return a; } function helper() { return; } function foo(i: number, j: number) { if (i > j) { return console.log(i); } else { return console.log(j); } } export function returnVoid(): void { console.log('before'); foo(1, 2); return helper(); console.log('after'); } function helper1(a: number) { if (a > 0) { return a; return 'hi'; } } export function deadReturnStatement(a: number) { console.log(helper1(a)); } export function returnNaN() { const a = NaN; return a; } export function returnInfiity() { const a = Infinity; return a; } export function returnNegInfiity() { const a = -Infinity; return a; } ================================================ FILE: tests/samples/sample_cases.test.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { testCompile } from '../utils/test_helper.js'; import 'mocha'; import { expect } from 'chai'; import { fstat, readdirSync } from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const IGNORE_LIST = [ "complexType_case1.ts", "complexType_case2.ts", "global_generics_function.ts", "inner_generics_function.ts", "namespace_generics_function.ts", "exception_custom_error.ts", "exception_throw_error.ts", "ignore_parameter_in_variable.ts" ] const STRINGREF_LIST = [ "for_in.ts", "obj_elem_get_and_set.ts" ] describe('basic_cases', function () { this.timeout(50000); readdirSync(__dirname) .filter((d) => { return d.endsWith('.ts') && !d.endsWith('.test.ts'); }) .forEach((f) => { let addTestFunc : any = it; if (IGNORE_LIST.includes(f)) { addTestFunc = it.skip; } addTestFunc(`${f}`, function () { expect(testCompile(path.join(__dirname, f))).eq(true); }); }); }); ================================================ FILE: tests/samples/scoped_variables.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ /* eslint-disable @typescript-eslint/no-empty-function */ export function nestedScopes() { let x = 1; { let x = 3; do { let x = 5; return x; } while (true); } } ================================================ FILE: tests/samples/shorthand_prop_assign.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ interface StringObject { [key: string]: string } interface PageInfo { name: string // template: NodeInfo styles: Array } export function node_undefined(): string { let styles = new Array() let a: StringObject = { name: 'level', country: 'China' }; styles.push(a); // let template: NodeInfo = buildNode(ele, styles) let page: PageInfo = { name: "/Home/index.json", // template, styles } const result: string = JSON.stringify(page) console.log(page.name); // /Home/index.json return result // ref.struct } ================================================ FILE: tests/samples/spread_operator.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A { name: string; constructor(name: string) { this.name = name; } } interface IA { name: string; } export function spread_number_array() { let a = [1, 2, 3]; let b = [10, ...a, 20, ...a]; console.log(b.length); // 8 console.log(b[2]); // 2 let c = [...a, 10]; console.log(c.length); // 4 console.log(c[0]); // 1 let d: number[] = []; let e = [...d]; console.log(e.length); // 0 let f: any = []; let g = [...f]; let h: any = [...f]; console.log(g.length); // 0 console.log(h.length); // 0 } export function spread_boolean_array() { let a = [true, false]; let b = [false, ...a, ...a]; console.log(b.length); // 5 console.log(b[1]); // true console.log(b[2]); // false } export function spread_string_array() { let a = ["1", "2"]; let b = ["3", ...a]; console.log(b.length); // 3 console.log(b[1]); // 1 } export function spread_object_array() { let a = [new A("A1"), new A("A2")]; let b = [new A("A3"), ...a]; console.log(b.length); // 3 console.log(b[1].name); // A1 b[1].name = "A4"; console.log(b[1].name); // A4 console.log(a[0].name); // A4 } export function spread_interface_array() { let a: IA[] = [new A("A1"), new A("A2")]; let b: IA[] = [new A("A3"), ...a]; console.log(b.length); // 3 console.log(b[1].name); // A1 } export function spread_literal_array() { let a = [1, ...[1, 2, 3], 4]; console.log(a.length); console.log(a[1]) console.log(a[2]); console.log(a[4]); let b = ["1", ...["1", "2"]]; console.log(b.length); console.log(b[1]); console.log(b[2]); let c = [true, ...[false, true]]; console.log(c.length); console.log(c[1]); console.log(c[2]); let d = [new A("A1"), ...[new A("A2"), new A("A3")]]; console.log(d.length); console.log(d[1].name); console.log(d[2].name); let e = [1, ...[...[1, 2]]]; console.log(e.length); console.log(e[1]); console.log(e[2]); let f = [1, 2, 3]; let g = [1, ...[f.pop()]]; console.log(f.length); console.log(f[1]); console.log(g[1]); } export function spread_any_array() { let a: any = [1, 2, 3]; let b = [10, ...a]; console.log(b.length); // 4 console.log(b[1]); // 1 a = [1, "2", new A("A1")]; b = [1, ...a]; console.log(b.length); // 4 console.log(b[3].name); // A1 let c: any = [...a, 10, ...a]; console.log(c.length); // 7 console.log(c[2].name); // A1 console.log(c[3]); // 10 } export function spread_nested_array() { let a: any = [1, 2]; let b = [a, 3]; let c = [...b, 4]; console.log(c.length); console.log(c[0][1]); console.log(c[2]); c[0][0] = 5; console.log(a[0]) } function test1(arg: number, ...args: number[]) { console.log(args.length); console.log(args[0]); console.log(args[args.length - 1]); } function test2(arg: string, ...args: string[]) { console.log(args.length); console.log(args[0]); console.log(args[args.length - 1]); } function test3(arg: A, ...args: A[]) { console.log(args.length); console.log(args[0].name); console.log(args[args.length - 1].name); } function test4(arg: IA, ...args: IA[]) { console.log(args.length); console.log(args[0].name); console.log(args[args.length - 1].name); } function test5(arg: any, ...args: any[]) { console.log(args.length); console.log(args[0]); console.log(args[1].name); console.log(args[args.length - 1].name); } export function pass_spread_to_rest_param() { let a = [1, 2, 3]; test1(10, 20, ...a, 30); let b = ["1", "2", "3"]; test2("10", "20", ...b); let c = [new A("A1"), new A("A2")]; test3(new A("A10"), new A("A20"), ...c, new A("A30")); test4(new A("A10"), new A("A20"), ...c); let d: any[] = [1, 2, 3, "3", true, new A("A1")]; test5(1, 2, new A("A3"), ...d); } ================================================ FILE: tests/samples/string_binary_operation.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function staticStringAdd() { const a = "hello"; const b = "world"; const c = a + b; console.log(c); } export function dynStringAdd() { const a: any = "hello"; const b: any = "world"; const c = a + b; console.log(c); } export function staticDynStringAdd() { const a: any = "hello"; const b = "world"; const c = a + b; console.log(c); } export function staticToStringAdd() { const a = "hello"; const b1 = 123; const c1 = a + b1; console.log(c1); const b2 = true; const c2 = a + b2; console.log(c2); } export function dynToStringAdd() { const a: any = "hello"; const b1: any = 123; const c1 = a + b1; console.log(c1); const b2: any = true; const c2: any = a + b2; console.log(c2); } ================================================ FILE: tests/samples/string_or.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function string_or() { const str1: string = '' const newStr1: string = str1 || "hello" console.log(newStr1) // hello const str2: string = 'wasm' const newStr2: string = str2 || "hello" console.log(newStr2) // wasm } ================================================ FILE: tests/samples/string_type.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function stringNotReturned() { const a: string = 'hello'; } export function returnString(): string { const a: string = 'hello'; return a; } export function assignStringToVariable() { let a: string; a = 'hello'; return a; } export function noExplicitStringKeyword() { const a = ''; return a; } export function unicode() { const a: string = '🀄'; return a; } export function noSubstitutionTplLiteral() { const a = `hello`; console.log(a); const b = ``; console.log(b); const c = ` This is a multiline string.`; console.log(c); } // string template function helper(str: string, num: number) { return `${str} is not ${num}`; } class A { x = 'hi'; y = `${this.x} x`; get xx() { return `${this.x} is x`; } static staticField = 10; } interface I { x: string; y: string; get xx(): string; set xx(value: string); } function foo() { const x = 10; return `${x} is 10`; } function outer() { const x = 0; function inner() { return `${x} is 0`; } return inner; } export function templateString() { const num = 1, str = 'Hello'; const tplStrAsStr = `${num} world`; const tplNumAsStr = `${num + 10} world`; const tplStrAsStr2 = `${str} world ${str} world`; console.log(tplStrAsStr); console.log(tplNumAsStr); console.log(tplStrAsStr2); // parameter as tpl str console.log(helper('hello', 10)); // class property as template string const a = new A(); a.x = `${num} and ${str}`; console.log(a.y); console.log(a.xx); console.log(`${A.staticField}`); // interface related template string const i: I = new A(); console.log(`${i.x}`); console.log(`${i.xx}`); // function call as template string console.log(`${foo()}`); console.log(`${outer()()}`); const anyFunc: any = foo; console.log(`${anyFunc()}`); // array as template string const arr = [`${num}`, `2`]; console.log(arr.toString()); console.log(`${arr[1]}`); // any as template string let obj: any = 1; console.log(`${obj} is 1`); obj = new A(); console.log(`${obj} is object`); obj.dy = 'hi'; console.log(`${obj.dy} is hi`); obj.dz = 0; console.log(`${obj.dz} is 0`); console.log(`${obj.du} is undefined`); } export function stringContainHex() { let s: string = "\x41B\x43"; console.log(s); } ================================================ FILE: tests/samples/switch_case_statement.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function switchWithDefault(): number { let i = 10; let j = 0; switch (i) { case 1: { j = 10; break; } case 11: { j = 11; break; } default: { j = 0; break; } } return j; } export function nestedSwitchCase(): number { let c = 100; let i = 10; let k = 13; switch (i) { case 11: { c = 101; break; } case 10: { for (let m = 0; m < 5; m++) { switch (m) { case 2: c++; break; case 1: { c = 0; } break; default: break; } } break; } } return c; } export function emptySwitch() { const i = 0; switch (i) { } return i; } export function switchWithoutDefault(): number { let i = 10; let j = 0; switch (i) { case 10: { j = 10; break; } case 11: { j = 11; break; } } return j; } export function multipleCasesShareSameBlock(): number { let i = 10; let j = 0; switch (i) { case 10: case 11: { j = 11; break; } default: { j = 12; break; } } return j; } export function caseWithoutBlock(): number { let i = 10; let j = 0; switch (i) { case 11: j = 11; break; case 10: j = 10; break; } return j; } export function caseWithoutBreak(): number { let i = 10; let j = 0; switch (i) { case 10: j = 10; case 11: j = 11; break; } return j; } export function varDeclarationInCase() { let i: number = 1; switch (i) { case 4: { i = 2; break; } case 2: { i = 3; break; } case 1: { j = 20; i = j; var j = 10; break; } } return i; } export function stringInCase() { let tag:string = 'bbb'; switch(tag) { case 'aaa': console.log('-aaa-', tag) break; case 'bbb': console.log('-bbb-', tag) break; default: console.log('-ccc-', tag) break; } } export function noCase() { let a = 100; switch (a) { default: { a = 10; } } console.log(a); } export function caseAndDefault() { let a = 100; switch (a) { case 100: { a += 20; } default: { a += 10; } } console.log(a); } ================================================ FILE: tests/samples/this_in_closure.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class Home { printTag(str:string){ console.log(str); } element(id: number) { console.log(id); } render() { const arr: string[] = ['start', 'stop', 'pause', 'resume'] arr.forEach((val, index, arr) => { const node_event = { event: (text: string, hh: number) => { this.element(hh); } } this.printTag(val); }); } } export function useThisInClosure() { let home = new Home(); home.render(); } ================================================ FILE: tests/samples/this_in_method.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A1 { func_a() { console.log('Class method'); } test() { return { func_a() { console.log('ObjLiteral method'); }, event: () => { this.func_a(); }, }; } } export function thisInObjLiteralArrowFunc() { const a = new A1(); a.test().event(); } class A2 { func_a() { console.log('Class method'); } test() { return { func_a() { console.log('ObjLiteral method'); }, event: function(){ this.func_a(); }, }; } } export function thisInObjLiteralFuncExpr() { const a = new A2(); a.test().event(); } ================================================ FILE: tests/samples/toString.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A { // tslint:disable-next-line: no-empty } interface I { // tslint:disable-next-line: no-empty } export function toStringTest() { const a = '1'; const b = 1; console.log(a + b.toString() === a + b); const c = false; console.log(a + c === a + c.toString()); const d = ['1', '2d', '3']; console.log(a + d === a + d.toString()); console.log(a + d.toString().length); const e = { a: 1, b: 2 }; console.log(a + e == a + e.toString()); const f = new A(); console.log(a + f == a + f.toString()); const g: I = f; console.log(a + g ==a + g.toString()); const h = () => { return 10; } console.log(a + h == a + h.toString()); let i: A | number = 10; console.log(a + i.toString()); i = new A(); console.log(a + i.toString()); let j: any = undefined; console.log(a + j.toString()); j = null; console.log(a + j.toString()); j = ['1', '2']; console.log(a + j.toString()); j = () => { // } console.log(a + j.toString()); j = 10; console.log(a + j.toString()); j = new A(); console.log(a + j.toString()); const str1 = 'start'; const str2 = 'middle'; console.log(str1 + str2); console.log(2001 + ': A Space Odyssey'); } ================================================ FILE: tests/samples/top_level_statements.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ let i: number; for (i = 0; i < 10; i++) { console.log(i); } console.log('Hello World'); ================================================ FILE: tests/samples/tuple.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function tuple_type_with_constant() { const a: [i32, f32] = [10, 20.5]; const b: [[i32, f32], string] = [a, 'hi']; console.log(b[0][0]); console.log(b[0][1]); console.log(b[1]); } export function tuple_type_with_variable() { const aaa = { a: 10 }; type tupleType = [any, string, i32]; const tupleInstance: tupleType = [aaa, 'hi', 90]; for (let i = 0; i < tupleInstance.length; i++) { const field = tupleInstance[i]; console.log(field); } } export function tuple_type_nested() { type tupleType = [string, [i32, boolean, [i64, [f32, any]]]]; const tupleInstance: tupleType = ['hi', [10, true, [20, [30, 'hello']]]]; const a_idx = 1; const b_idx = 2; const c_idx = 0; /* TODO: tuple box to any & unbox from any is not ready */ // return tupleInstance[a_idx][b_idx][c_idx]; } function tuple_as_param_inner(tuple: [i32, string]) { console.log(tuple[0]); console.log(tuple[1]); } export function tuple_as_param() { const param: [i32, string] = [30, 'hi']; tuple_as_param_inner(param); } interface I { a: i32 } function tuple_as_ret_inner(): [I, i64] { const obj:I = { a: 10 as i32 } const tuple: [I, i64] = [obj, 100] return tuple; } export function tuple_as_ret() { const tuple: [I, i64] = tuple_as_ret_inner(); const obj = tuple[0]; console.log(obj.a); console.log(tuple[1]); } export function tuple_as_array_elem() { const tuple1: [i32, string] = [1, 'hi_1']; const tuple2: [i32, string] = [2, 'hi_2']; const tuple3: [i32, string] = [3, 'hi_3']; const array: [i32, string][] = [tuple1, tuple2, tuple3]; console.log(array[0][1]); console.log(array[1][1]); console.log(array[2][1]); const iterable: [i64, string][] = [[1, 'value1']]; console.log(iterable[0][0]); } export function tuple_as_obj_field() { const tuple1: [i32, string] = [1, 'hi_1']; const tuple2: [i32, string] = [2, 'hi_2']; const tuple3: [i32, string] = [3, 'hi_3']; const obj = { a: tuple1 as [i32, string], b: tuple2 as [i32, string], c: tuple3 as [i32, string], } console.log(obj.a[1]); console.log(obj.b[1]); console.log(obj.c[1]); } interface T { x: [i32, string], y: [number, string], } export function tuple_as_infc_field() { const tuple1: [i32, string] = [1, 'hi_1']; const tuple2: [number, string] = [2, 'hi_2']; const obj: T = { x: tuple1 as [i32, string], y: tuple2, } /* TODO: tuple box to any & unbox from any is not ready */ // console.log(obj.x[1]); // console.log(obj.y[1]); } export function tuple_with_array() { const array1: i32[] = [1, 2, 3]; const array2: string[] = ['hi_1', 'hi_2', 'hi_3']; const tuple: [i32[], string[]] = [array1, array2]; console.log(tuple[0][1]); console.log(tuple[1][1]); } class A { a: i64 = 1; b: string = 'hi_1'; } class B { a: i64 = 2; b: string = 'hi_2'; } export function tuple_with_class() { const a_instance = new A(); const b_instance = new B(); const tuple: [A, B] = [a_instance, b_instance]; console.log(tuple[0].a); console.log(tuple[1].b); } export function tuple_with_infc() { const tuple1: [i32, string] = [1, 'hi_1']; const tuple2: [number, string] = [2, 'hi_2']; const obj: T = { x: tuple1 as [i32, string], y: tuple2, } const tuple: [T] = [obj]; /* TODO: tuple box to any & unbox from any is not ready */ // console.log(tuple[0].x[0]); } ================================================ FILE: tests/samples/typealias.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ type int = number; type pet = 'cat' | 'dog'; interface Point { x: T; y: T; } type IntPoint = Point; type num = 1 | 2; // number type bool = true | false; // boolean type obj = {a: 1} | {b: 2}; // object type func = (() => void); // function function pet_point(p: pet, point: IntPoint) { console.log(p, point.x, point.y); } function num_add(n1: num, n2: num): number { return n1 + n2; } function bool_test(b: boolean): number { return b ? 1 : 0; } function obj_test(o: obj) { console.log(o); } function func_test(f: func) { f(); } type newA = () => A; const a: newA = () => { return new A(); }; function func() { return new A(); } export function useTypeBeforeDefine() { a(); func(); } class A { constructor() { console.log('A'); } } ================================================ FILE: tests/samples/typeof.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class D { // } interface G { // } export function typeofTest() { const a = 10; const b = '123'; const c = false; const d = new D(); const e = new Array(); const f = { a: 1 }; const g: G = new D(); const h = null; const i = undefined; const j: 1 | 2 = 1; let k: string | D = '123'; const l: any = new Array(); const m: any = { aa: 1 }; const n: any = 1; const o: any = '123'; const p: any = () => { // }; console.log(typeof a); console.log(typeof b); console.log(typeof c); console.log(typeof d); console.log(typeof e); console.log(typeof f); console.log(typeof g); console.log(typeof h); console.log(typeof i); console.log(typeof j); console.log(typeof l); console.log(typeof m); console.log(typeof n); console.log(typeof o); console.log(typeof p); if (typeof k === 'string') { k = new D(); console.log(typeof k); } } ================================================ FILE: tests/samples/unary_operator.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ let globalIndex: number = 3 function increasePre(): number { return ++globalIndex; } function increasePost(): number { return globalIndex++; } function decreasePre(): number { return --globalIndex; } function decreasePost(): number { return globalIndex--; } function plusplusTest() { let ret = increasePre(); console.log(ret); console.log(globalIndex); ret = increasePost(); console.log(ret); console.log(globalIndex); let localIndex = 0; console.log(localIndex++); console.log(++localIndex); } function minusminusTest() { let ret = decreasePre(); console.log(ret); console.log(globalIndex); ret = decreasePost(); console.log(ret); console.log(globalIndex); let localIndex = 2; console.log(localIndex--); console.log(--localIndex); } function exclamationTest() { const f = 0; console.log(!f); const t = 1; console.log(!t); } function minusTest() { const a = 1; console.log(-a); const b = -2; console.log(-b); } export function test() { plusplusTest(); minusminusTest(); exclamationTest(); minusTest(); } ================================================ FILE: tests/samples/undefined_test.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function test() { let temp; console.log(temp); var age = 18; var sex; console.log(age, sex); } ================================================ FILE: tests/samples/union_assign.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function unionTypeAssign(){ const a: number|string = 100; let res: number|string; res = a; return res as number; } ================================================ FILE: tests/samples/union_field_get.ts ================================================ /* * Copyright (C) 2023 Xiaomi Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ class A { x = 10; static fooA() { return 10; } } class B { y = 20; static fooB() { return 20; } } function test1() { const a: A | null = new A(); return a.x; } function test2() { let a: A | number = new A(); a = a.x + 10; return a; } function test3() { let a: A | B | null = new A(); let x = a.x; a = new B(); let y = a.y; } function test4() { let a: A | B | string = new A(); let x = a.x; a = new B(); let y = a.y; a = "hello world"; } function test_any_null() : any | null { return null; } export function test_func_return_any_null() { if (test_any_null() === null) { console.log("is null"); } else { console.log("not null"); } } ================================================ FILE: tests/samples/union_func_call.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ function testFunc(a: boolean) { if (a) { return 10; } return 11; } export function unionFuncCall() { let fn: undefined | ((a: boolean)=> number) = testFunc; const a = fn(true); console.log(a); } ================================================ FILE: tests/samples/unsigned_value.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function random_f64() { let m_w: f64 = 123456789; let m_z: f64 = 987654321; let mask: f64 = 0xffffffff; m_z = (36969 * (m_z & 65535) + (m_z >> 16)) & mask; m_w = (18000 * (m_w & 65535) + (m_w >> 16)) & mask; let result: f64 = (((m_z << 16) + (m_w & 65535)) >>> 0) / 4294967296; return result; } export function random_i64() { let m_w: i64 = 123456789; let m_z: i64 = 987654321; let mask: i64 = 0xffffffff; m_z = (36969 * (m_z & 65535) + (m_z >> 16)) & mask; m_w = (18000 * (m_w & 65535) + (m_w >> 16)) & mask; let result: i64 = (((m_z << 16) + (m_w & 65535)) >>> 0) / 4294967296; return result; } export function random_i32() { let m_w: i32 = 123456789; let m_z: i32 = 987654321; let mask: i32 = 0xffffffff; m_z = (36969 * (m_z & 65535) + (m_z >> 16)) & mask; m_w = (18000 * (m_w & 65535) + (m_w >> 16)) & mask; let result: i32 = (((m_z << 16) + (m_w & 65535)) >>> 0) / 4294967296; return result; } export function random_f32() { let m_w: f32 = 123456789; let m_z: f32 = 987654321; let mask: f32 = 0xffffffff; m_z = (36969 * (m_z & 65535) + (m_z >> 16)) & mask; m_w = (18000 * (m_w & 65535) + (m_w >> 16)) & mask; let result: f32 = (((m_z << 16) + (m_w & 65535)) >>> 0) / 4294967296; return result; } ================================================ FILE: tests/samples/wasmType_basic.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function wasmTypeAssign() { /* assignment */ let i32Value: i32 = 132; let i64Value: i64 = 164; let f32Value: f32 = 232.25252525; let f64Value: f64 = 264.75757575; let anyValue: anyref = 'hi'; console.log(i32Value); console.log(i64Value); console.log(f32Value); console.log(f64Value); console.log(anyValue); } export function toI32Value() { let i32Value: i32 = 132; let i64Value: i64 = 164; let f32Value: f32 = 232.25252525; let f64Value: f64 = 264.75757575; /* convert to i32 */ i32Value = i64Value; console.log(i32Value); i32Value = f32Value; console.log(i32Value); i32Value = f64Value; console.log(i32Value); } export function toI64Value() { let i32Value: i32 = 132; let i64Value: i64 = 164; let f32Value: f32 = 232.25252525; let f64Value: f64 = 264.75757575; /* convert to i64 */ i64Value = i32Value; console.log(i64Value); i64Value = f32Value; console.log(i64Value); i64Value = f64Value; console.log(i64Value); } export function toF32Value() { let i32Value: i32 = 132; let i64Value: i64 = 164; let f32Value: f32 = 232.25252525; let f64Value: f64 = 264.75757575; /* convert to f32 */ f32Value = i32Value; console.log(f32Value); f32Value = i64Value; console.log(f32Value); f32Value = f64Value; console.log(f32Value); } export function toF64Value() { let i32Value: i32 = 132; let i64Value: i64 = 164; let f32Value: f32 = 232.32; let f64Value: f64 = 264.64; /* convert to f64 */ f64Value = i32Value; console.log(f64Value); f64Value = i64Value; console.log(f64Value); f64Value = f32Value; console.log(f64Value); } export function anyConvertValue() { let anyValue: anyref = 'hi'; console.log(anyValue); let f32Value: f32 = 900; anyValue = f32Value; console.log(f32Value); console.log(anyValue); anyValue = 800; f32Value = anyValue; console.log(f32Value); console.log(anyValue); } export function optimizeLiteralValue() { const i32Value: i32 = 100; console.log(i32Value); const i64Value: i64 = 100; console.log(i64Value); const f32Value: f32 = 100; console.log(f32Value); const f64Value: f64 = 100; console.log(f64Value); } export function operateWasmTypeAndLiteral() { let i32Value: i32 = 10; let i64Value: i64 = 20; let f32Value: f32 = 30.50; let f64Value: f64 = 40.550; const res0 = i32Value + 100; const res1 = i32Value + 100.80; const res2 = i64Value + 100; const res3 = i64Value + 100.80; const res4 = f32Value + 100; const res5 = f32Value + 100.80; const res6 = f64Value + 100; const res7 = f64Value + 100.80; console.log(res0) console.log(res1) console.log(res2) console.log(res3) console.log(res4) console.log(res5) console.log(res6) console.log(res7) } export function operateWasmTypeAndWasmType() { let i32Value: i32 = 10; let i64Value: i64 = 20; let f32Value: f32 = 30.50; let f64Value: f64 = 40.5550; const res0 = i32Value + i32Value; const res1 = i32Value + i64Value; const res2 = i32Value + f32Value; const res3 = i32Value + f64Value; const res4 = i64Value + i32Value; const res5 = i64Value + i64Value; const res6 = i64Value + f32Value; const res7 = i64Value + f64Value; const res8 = f32Value + i32Value; const res9 = f32Value + i64Value; const res10 = f32Value + f32Value; const res11 = f32Value + f64Value; const res12 = f64Value + i32Value; const res13 = f64Value + i64Value; const res14 = f64Value + f32Value; const res15 = f64Value + f64Value; console.log(res0) console.log(res1) console.log(res2) console.log(res3) console.log(res4) console.log(res5) console.log(res6) console.log(res7) console.log(res8) console.log(res9) console.log(res10) console.log(res11) console.log(res12) console.log(res13) console.log(res14) console.log(res15) } export function wasmTypeCompare() { let a: i64 = 0; if (a < 100) { console.log(a); } } export function wasmTypeUnaryExpr() { let a: i64 = 0; a++; console.log(a); --a; console.log(a); if (!a) { console.log('hi'); } else { console.log('hello'); } } export function xor() { // i32 const a32: i32 = 4; const b32: i32 = 0x1234; console.log(a32 ^ b32); // i64 const a64: i64 = 0xffffffff; const b64: i64 = 1; console.log(a64 ^ b64); // f32 const af32: f32 = 4.1; const bf32: f32 = 0x1234; console.log(af32 ^ bf32); } export function and() { // i32 const a32: i32 = 4; const b32: i32 = 0x1234; console.log(a32 & b32); // i64 const a64: i64 = 0xffffffff; const b64: i64 = 0xffffffff; console.log(a64 & b64); // f32 const af32: f32 = 4.1; const bf32: f32 = 0x1234; console.log(af32 & bf32); } export function or() { // i32 const a32: i32 = 4; const b32: i32 = 0x1234; console.log(a32 | b32); // i64 const a64: i64 = 0xffffffff; const b64: i64 = 0xffffffff; console.log(a64 | b64); // f32 const af32: f32 = 4.1; const bf32: f32 = 0x1234; console.log(af32 | bf32); } export function shl() { // i32 const a32: i32 = 0x7fffffff; const b32: i32 = 1; console.log(a32 << b32); // i64 const a64: i64 = 0x7fffffff; const b64: i64 = 1; console.log(a64 << b64); // f32 const af32: f32 = 4.1; const bf32: f32 = 1; console.log(af32 << bf32); } export function shr() { // i32 const a32: i32 = 0x7fffffff; const a32n: i32 = -0x7fffffff; const b32: i32 = 1; console.log(a32 >> b32); console.log(a32n >>> b32); // i64 const a64: i64 = 0xfffffffff; const a64n: i64 = 0xfffffffff; const b64: i64 = 1; console.log(a64 >> b64); console.log(a64n >>> b64); // f32 const af32: f32 = 4.1; const af32n: f32 = -4.1; const bf32: f32 = 1; console.log(af32 >> bf32); console.log(af32n >>> bf32); } ================================================ FILE: tests/samples/wasmType_heapType.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function wasmArrayTypeWithLiteral() { // Wasmnizer-ts: @WASMArray@ type arrayType1 = string[]; const keys_arr: arrayType1 = ['hi']; const value1 = keys_arr[0]; console.log(value1); keys_arr[0] = 'hello'; const value2 = keys_arr[0]; console.log(value2); // Wasmnizer-ts: @WASMArray@ type arrayType2 = i32[]; const arr: arrayType2 = [88]; const value3 = arr[0]; console.log(value3); arr[0] = 77; const value4 = arr[0]; console.log(value4); } export function wasmArrayTypeWithNewArray() { const arrLen = 3; // Wasmnizer-ts: @WASMArray@ type arrayType = i64[]; const wasmArr: arrayType = new Array(arrLen); for (let i = 0; i < arrLen; i++) { wasmArr[i] = i; } for (let i = 0; i < arrLen; i++) { console.log(wasmArr[i]); } const wasmArr2: arrayType = new Array(8, 9); for (let i = 0; i < wasmArr2.length; i++) { console.log(wasmArr2[i]); } } export function wasmArrayTypeNested(): i64 { // Wasmnizer-ts: @WASMArray@ type arrayType1 = i64[]; const wasmArr1: arrayType1 = [1, 10]; // Wasmnizer-ts: @WASMArray@ type arrayType2 = arrayType1[]; const wasmArr2: arrayType2 = [wasmArr1]; return wasmArr2[0][0]; } export function wasmArrayTypeInArray(): i64 { // Wasmnizer-ts: @WASMArray@ type arrayType1 = i64[]; const wasmArr1: arrayType1 = [1, 10]; const arr: arrayType1[] = [wasmArr1]; return arr[0][0]; } export function wasmArrayTypeInObj(): i64 { // Wasmnizer-ts: @WASMArray@ type arrayType1 = i64[]; const wasmArr1: arrayType1 = [1, 10]; const obj = { a: wasmArr1 as arrayType1 } return obj.a[0]; } export function wasmStructType() { // Wasmnizer-ts: @WASMArray@ type arrayType1 = f32[]; const arr1: arrayType1 = [10]; type arrayType2 = arrayType1; const arr2: arrayType2 = [20]; // Wasmnizer-ts: @WASMStruct@ <[Not_Packed, Not_Packed], [Mutable, Mutable], Nullable, NULL> type structType1 = [arrayType1, i64]; const struct: structType1 = [arr1, 99]; const value1 = struct[0][0]; console.log(value1); const value2 = struct[1]; console.log(value2); // Wasmnizer-ts: @WASMStruct@ type structType2 = [arrayType2, i32]; const struct2: structType2 = [arr2, 33]; const value3 = struct2[0][0]; console.log(value3); const value4 = struct2[1]; console.log(value4); const value5 = struct2.length; console.log(value5); } export function wasmStructTypeNested(): i64 { // Wasmnizer-ts: @WASMStruct@ type structType1 = [i64]; const wasmStruct1: structType1 = [1]; // Wasmnizer-ts: @WASMStruct@ type structType2 = [structType1]; const wasmStruct2: structType2 = [wasmStruct1]; return wasmStruct2[0][0]; } export function wasmStructTypeInArray(): i64 { // Wasmnizer-ts: @WASMStruct@ type structType1 = [i64]; const wasmStruct1: structType1 = [1]; const arr: structType1[] = [wasmStruct1]; return arr[0][0]; } export function wasmStructTypeInObj(): i64 { // Wasmnizer-ts: @WASMStruct@ type structType1 = [i64]; const wasmStruct1: structType1 = [1]; const obj = { a: wasmStruct1 as structType1 } return obj.a[0]; } ================================================ FILE: tests/samples/wasmType_in_otherType.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ export function NonEffectWasmType() { let nnn: number = 1; let nn2: i64 = 2; class A { field: number; constructor() { this.field = 1; } } console.log(nnn); } export function wasmTypeInClass() { let a: i32 = 10; let b: i64 = 20; let c: f32 = 30.50; let d: f64 = 40.450; class A { field1: i32; field2: i64; field3: f32; field4: f64; constructor() { this.field1 = 1; this.field2 = 2; this.field3 = 3.5; this.field4 = 5.5; } } const instance = new A(); console.log(instance.field1); console.log(instance.field2); console.log(instance.field3); console.log(instance.field4); instance.field1 = a; instance.field2 = b; instance.field3 = c; instance.field4 = d; console.log(instance.field1); console.log(instance.field2); console.log(instance.field3); console.log(instance.field4); } export function wasmTypeInObj() { let a: i32 = 10; let b: i64 = 20; let c: f32 = 30.50; let d: f64 = 40.450; interface IA{ field1: i32; field2: i64; field3: f32; field4: f64; } const ia : IA = { field1 : 1 as i32, field2 : 2 as i64, field3 : 3.5 as f32, field4 : 5.5 as f64, }; console.log(ia.field1); console.log(ia.field2); console.log(ia.field3); console.log(ia.field4); ia.field1 = a; ia.field2 = b; ia.field3 = c; ia.field4 = d; console.log(ia.field1); console.log(ia.field2); console.log(ia.field3); console.log(ia.field4); } export function wasmTypeInArray() { const arr1: i32[] = []; const tmpI32Value: i32 = 1; arr1.push(tmpI32Value); arr1.push(10); arr1.push(12.75); console.log(arr1[0]); console.log(arr1[1]); console.log(arr1[2]); const arr2: i64[] = []; arr2.push(2); arr2.push(5.89); console.log(arr2[0]); console.log(arr2[1]); const arr3: f32[] = [1, 2.987]; arr3.push(2); arr3.push(5.89); console.log(arr3[0]); console.log(arr3[1]); console.log(arr3[2]); console.log(arr3[3]); const arr4: f64[] = [1, 2.987]; arr4.push(2); arr4.push(5.89); console.log(arr4[0]); console.log(arr4[1]); console.log(arr4[2]); console.log(arr4[3]); const arr5: anyref[] = []; arr5.push(2); arr5.push(3.5); arr5.push('hi'); console.log(arr5[0]); console.log(arr5[1]); console.log(arr5[2]); } export function wasmTypeAs() { const a = 100.78; const b: i32 = a as i32; const c: i64 = a as i64; const d: f32 = a as f32; const e: f64 = a as f64; console.log(b); console.log(c); console.log(d); console.log(e); } export function wasmTypeI32AsReturnType(): i32 { return 100.25; } export function wasmTypeI64AsReturnType(): i64 { return 100.25; } export function wasmTypeF32AsReturnType(): f32 { return 100.25; } export function wasmTypeF64AsReturnType(): f64 { return 100.25; } ================================================ FILE: tests/unit/comment.test.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import 'mocha'; import { expect } from 'chai'; import { BlockNode, FunctionDeclareNode, FunctionOwnKind, NativeSignature, } from '../../src/semantics/semantics_nodes.js'; import { FunctionType, WASM } from '../../src/semantics/value_types.js'; import { GetBuiltinObjectType, builtinTypes, } from '../../src/semantics/builtin.js'; import { Export, Import, MutabilityKind, NativeSignature as NativeSignatureFrontEnd, NullabilityKind, PackedTypeKind, WASMArray, WASMStruct, getBuiltinType, isExportComment, isImportComment, isNativeSignatureComment, isWASMArrayComment, isWASMStructComment, parseComment, } from '../../src/utils.js'; import { FunctionalFuncs } from '../../src/backend/binaryen/utils.js'; import binaryen from 'binaryen'; import { arrayBufferTypeInfo } from '../../src/backend/binaryen/glue/packType.js'; import exp from 'constants'; describe('testParseNativeSignature', function () { it('ARRAYBUFFER_TO_I32', function () { const func = new FunctionDeclareNode( 'funcA', FunctionOwnKind.DEFAULT, new FunctionType(-1, WASM.I32, [ GetBuiltinObjectType('ArrayBuffer'), WASM.I32, ]), new BlockNode([]), ); const signatureComment: NativeSignature = { paramTypes: [WASM.I32, WASM.I32], returnType: WASM.I32, }; func.comments.push(signatureComment); const module = new binaryen.Module(); const innerOpStmts: binaryen.ExpressionRef[] = []; const calledParamValueRefs: binaryen.ExpressionRef[] = []; const vars: binaryen.ExpressionRef[] = []; const mallocOffsets: binaryen.ExpressionRef[] = []; FunctionalFuncs.parseNativeSignature( module, innerOpStmts, func.funcType.argumentsType, [arrayBufferTypeInfo.typeRef, binaryen.i32], signatureComment.paramTypes, 2, calledParamValueRefs, vars, mallocOffsets, true, ); expect(calledParamValueRefs.length).eq(2); expect(vars.length).eq(2); expect(vars[0]).eq(binaryen.i32); expect(vars[1]).eq(binaryen.i32); }); it('I32_TO_ARRAYBUFFER', function () { const func = new FunctionDeclareNode( 'funcA', FunctionOwnKind.DEFAULT, new FunctionType(-1, WASM.I32, [ GetBuiltinObjectType('ArrayBuffer'), WASM.I32, ]), new BlockNode([]), ); const signatureComment: NativeSignature = { paramTypes: [WASM.I32, WASM.I32], returnType: WASM.I32, }; func.comments.push(signatureComment); const module = new binaryen.Module(); const innerOpStmts: binaryen.ExpressionRef[] = []; const calledParamValueRefs: binaryen.ExpressionRef[] = []; const vars: binaryen.ExpressionRef[] = []; const mallocOffsets: binaryen.ExpressionRef[] = []; FunctionalFuncs.parseNativeSignature( module, innerOpStmts, signatureComment.paramTypes, [binaryen.i32, binaryen.i32], func.funcType.argumentsType, 2, calledParamValueRefs, vars, mallocOffsets, false, ); expect(calledParamValueRefs.length).eq(2); expect(vars.length).eq(2); expect(vars[0]).eq(arrayBufferTypeInfo.typeRef); expect(vars[1]).eq(binaryen.i32); }); }); describe('testParseComment', function () { it('parseNativeSignatureTrue', function () { const commentStr = '// Wasmnizer-ts: @NativeSignature@ (i32, i32)=>i32'; const res = parseComment(commentStr); const isNativeSignature = isNativeSignatureComment(res); const paramTypes = (res as NativeSignatureFrontEnd).paramTypes; const returnType = (res as NativeSignatureFrontEnd).returnType; expect(isNativeSignature).eq(true); expect(paramTypes.length).eq(2); expect(paramTypes[0]).eq(getBuiltinType('i32')); expect(paramTypes[1]).eq(getBuiltinType('i32')); expect(returnType).eq(getBuiltinType('i32')); const commentStr2 = '// Wasmnizer-ts: @NativeSignature@ (anyref, f64)=>number'; const res2 = parseComment(commentStr2); const isNativeSignature2 = isNativeSignatureComment(res2); const paramTypes2 = (res2 as NativeSignatureFrontEnd).paramTypes; const returnType2 = (res2 as NativeSignatureFrontEnd).returnType; expect(isNativeSignature2).eq(true); expect(paramTypes2.length).eq(2); expect(paramTypes2[0]).eq(getBuiltinType('anyref')); expect(paramTypes2[1]).eq(getBuiltinType('f64')); expect(returnType2).eq(getBuiltinType('number')); }); it('parseNativeSignatureTrueWithMultiTab', function () { const commentStr = '// Wasmnizer-ts: @NativeSignature@ (i32 , i32 )=> i32'; const res = parseComment(commentStr); const isNativeSignature = isNativeSignatureComment(res); const paramTypes = (res as NativeSignatureFrontEnd).paramTypes; const returnType = (res as NativeSignatureFrontEnd).returnType; expect(isNativeSignature).eq(true); expect(paramTypes.length).eq(2); expect(paramTypes[0]).eq(getBuiltinType('i32')); expect(paramTypes[1]).eq(getBuiltinType('i32')); expect(returnType).eq(getBuiltinType('i32')); }); it('parseNativeSignatureFalseWithWrongSpecificStr', function () { const commentStr = '//Wasmnizer-js: @NativeSignature@ (i32, i32)=>i32'; const res = parseComment(commentStr); expect(res).eq(null); const commentStr2 = '//Wasmnizer-ts: @NativeSignature (i32, i32)=>i32'; const res2 = parseComment(commentStr2); expect(res2).eq(null); const commentStr3 = '//Wasmnizer-ts: @NativeSignature@ (i32, i32)=i32'; const res3 = parseComment(commentStr3); expect(res3).eq(null); }); it('parseNativeSignatureFalseWithInValidType', function () { const commentStr = '//Wasmnizer-ts: @NativeSignature@ (ArrayBuffer)=>i32'; const res = parseComment(commentStr); expect(res).eq(null); const commentStr2 = '//Wasmnizer-ts: @NativeSignature (i32)=>ArrayBuffer'; const res2 = parseComment(commentStr2); expect(res2).eq(null); }); it('parseImportTrue', function () { const commentStr = '// Wasmnizer-ts: @Import@ wamr, nameH'; const res = parseComment(commentStr); const isImport = isImportComment(res); const moduleName = (res as Import).moduleName; const funcName = (res as Import).funcName; expect(isImport).eq(true); expect(moduleName).eq('wamr'); expect(funcName).eq('nameH'); }); it('parseImportFalse', function () { const commentStr = '// Wasmnizer-ts: @Impor@ wamr, nameH'; const res = parseComment(commentStr); expect(res).eq(null); const commentStr2 = '// Wasmnizer-ts: @Import@ wamr'; const res2 = parseComment(commentStr2); expect(res2).eq(null); const commentStr3 = '// Wasmnizer-ts: @Import@ wamr, n, q'; const res3 = parseComment(commentStr3); expect(res3).eq(null); }); it('parseExportTrue', function () { const commentStr = '// Wasmnizer-ts: @Export@ nameD'; const res = parseComment(commentStr); const isExport = isExportComment(res); const exportName = (res as Export).exportName; expect(isExport).eq(true); expect(exportName).eq('nameD'); }); it('parseExportFalse', function () { const commentStr = '// Wasmnizer-ts: @Export@ nameD, nameE'; const res = parseComment(commentStr); expect(res).eq(null); }); it('parseWASMArraySimpleInfo', function () { const commentStr = '// Wasmnizer-ts: @WASMArray@'; const res = parseComment(commentStr); const isWASMArray = isWASMArrayComment(res); const packedTypeKind = (res as WASMArray).packedType; const mutability = (res as WASMArray).mutability; const nullability = (res as WASMArray).nullability; expect(isWASMArray).eq(true); expect(packedTypeKind).eq(PackedTypeKind.Not_Packed); expect(mutability).eq(MutabilityKind.Mutable); expect(nullability).eq(NullabilityKind.Nullable); }); it('parseWASMArrayFullInfo', function () { const commentStr = '// Wasmnizer-ts: @WASMArray@ '; const res = parseComment(commentStr); const isWASMArray = isWASMArrayComment(res); const packedTypeKind = (res as WASMArray).packedType; const mutability = (res as WASMArray).mutability; const nullability = (res as WASMArray).nullability; expect(isWASMArray).eq(true); expect(packedTypeKind).eq(PackedTypeKind.Not_Packed); expect(mutability).eq(MutabilityKind.Mutable); expect(nullability).eq(NullabilityKind.Nullable); }); it('parseWASMStructSimpleInfo', function () { const commentStr = '// Wasmnizer-ts: @WASMStruct@ '; const res = parseComment(commentStr); const isWASMStruct = isWASMStructComment(res); expect(isWASMStruct).eq(true); }); it('parseWASMStructFullInfo', function () { const commentStr = '// Wasmnizer-ts: @WASMStruct@ <[I8, I16], [Mutable, Immutable], NonNullable, NULL>'; const res = parseComment(commentStr); const isWASMStruct = isWASMStructComment(res); const packedTypeKinds = (res as WASMStruct).packedTypes!; const mutabilitys = (res as WASMStruct).mutabilitys!; const nullability = (res as WASMStruct).nullability!; const baseTypeName = (res as WASMStruct).baseTypeName!; expect(isWASMStruct).eq(true); expect(packedTypeKinds.length).eq(2); expect(packedTypeKinds[0]).eq(PackedTypeKind.I8); expect(packedTypeKinds[1]).eq(PackedTypeKind.I16); expect(mutabilitys.length).eq(2); expect(mutabilitys[0]).eq(MutabilityKind.Mutable); expect(mutabilitys[1]).eq(MutabilityKind.Immutable); expect(nullability).eq(NullabilityKind.NonNullable); expect(baseTypeName).eq('NULL'); }); }); ================================================ FILE: tests/unit/expression.test.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import ts from 'typescript'; import 'mocha'; import { expect } from 'chai'; import { BinaryExpression, IdentifierExpression, NumberLiteralExpression, ObjectLiteralExpression, PropertyAccessExpression, StringLiteralExpression, } from '../../src/expression.js'; describe('testExpression', function () { it('validateExpressionRelation', function () { const numberLiteralExpression = new NumberLiteralExpression(3); const stringLiteralExpression = new StringLiteralExpression('test'); const identifierExpression = new IdentifierExpression('a'); const objectLiteralExpression = new ObjectLiteralExpression( [identifierExpression], [numberLiteralExpression], ); const binaryExpression = new BinaryExpression( ts.SyntaxKind.PlusToken, numberLiteralExpression, stringLiteralExpression, ); const propertyAccessExpression = new PropertyAccessExpression( objectLiteralExpression, identifierExpression, ); expect(identifierExpression.identifierName).eq('a'); expect(binaryExpression.leftOperand).eq(numberLiteralExpression); expect(binaryExpression.rightOperand).eq(stringLiteralExpression); expect(propertyAccessExpression.propertyAccessExpr).eq( objectLiteralExpression, ); expect(propertyAccessExpression.propertyExpr).eq(identifierExpression); }); }); ================================================ FILE: tests/unit/scope.test.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import ts from 'typescript'; import 'mocha'; import { expect } from 'chai'; import { GlobalScope, FunctionScope, BlockScope, ScopeKind, } from '../../src/scope.js'; import { Variable, Parameter, ModifierKind } from '../../src/variable.js'; import { Primitive, TSFunction, Type } from '../../src/type.js'; import { Statement } from '../../src/statement.js'; import { Expression } from '../../src/expression.js'; import { mangling } from '../../src/utils.js'; describe('testScope', function () { it('nestedScope', function () { const globalScope = new GlobalScope(); const funcScope = new FunctionScope(globalScope); const blockScope = new BlockScope(funcScope); expect(globalScope.parent).eq(null); expect(funcScope.parent).eq(globalScope); expect(blockScope.parent).eq(funcScope); }); it('scopeKind', function () { const globalScope = new GlobalScope(); const funcScope = new FunctionScope(globalScope); const blockScope = new BlockScope(funcScope); expect(globalScope.kind).eq(ScopeKind.GlobalScope); expect(funcScope.kind).eq(ScopeKind.FunctionScope); expect(blockScope.kind).eq(ScopeKind.BlockScope); }); it('findVariableInScope', function () { const globalScope = new GlobalScope(); const var1 = new Variable('var1', new Type(), [], 0); globalScope.addVariable(var1); const funcScope = new FunctionScope(globalScope); const var2 = new Variable('var2', new Type(), [], 0); funcScope.addVariable(var2); const blockScope = new BlockScope(funcScope); const var3 = new Variable('var3', new Type(), [], 0); blockScope.addVariable(var3); expect(globalScope.findVariable('var1')).eq(var1); expect(globalScope.findVariable('var2')).eq(undefined); expect(globalScope.findVariable('var3')).eq(undefined); expect(funcScope.findVariable('var1')).eq(var1); expect(funcScope.findVariable('var2')).eq(var2); expect(funcScope.findVariable('var3')).eq(undefined); expect(blockScope.findVariable('var1')).eq(var1); expect(blockScope.findVariable('var2')).eq(var2); expect(blockScope.findVariable('var3')).eq(var3); }); it('findVariableAcrossModule', function () { const globalScope2 = new GlobalScope(); globalScope2.moduleName = 'module2'; const global2var1 = new Variable('global2var1', new Type(), [], 0); globalScope2.addVariable(global2var1); const globalScope = new GlobalScope(); globalScope.moduleName = 'module1'; globalScope.addImportIdentifier('global2var1', globalScope2); const var1 = new Variable('var1', new Type(), [], 0); globalScope.addVariable(var1); const funcScope = new FunctionScope(globalScope); const var2 = new Variable('global2var1', new Type(), [], 0); const param1 = new Parameter('parma1', new Type(), [], 0, true, false); funcScope.addVariable(var1); funcScope.addVariable(var2); funcScope.addParameter(param1); expect(globalScope.findVariable('var1')).eq(var1); expect(globalScope.findVariable('var2')).eq(undefined); expect(globalScope.findIdentifier('global2var1')).eq(global2var1); expect(funcScope.findVariable('var1')).eq(var1); expect(funcScope.findVariable('global2var1')).eq(var2); expect(funcScope.findVariable('parma1')).eq(param1); }); it('findParameterInScope', function () { const globalScope = new GlobalScope(); const funcScope = new FunctionScope(globalScope); const blockScope = new BlockScope(funcScope); const param1 = new Parameter('param1', new Type(), [], 0, false, false); funcScope.addVariable(param1); expect(globalScope.findVariable('param1')).eq(undefined); expect(funcScope.findVariable('param1')).eq(param1); expect(blockScope.findVariable('param1')).eq(param1); }); it('getParentAndChildren', function () { const globalScope = new GlobalScope(); const functionScope = new FunctionScope(globalScope); const blockScope = new BlockScope(functionScope); expect(globalScope.parent).eq(null); expect(globalScope.children[0]).eq(functionScope); expect(functionScope.parent).eq(globalScope); expect(functionScope.children[0]).eq(blockScope); expect(blockScope.parent).eq(functionScope); expect(blockScope.children.length).eq(0); }); it('judgeScopeKind', function () { const globalScope = new GlobalScope(); const functionScope = new FunctionScope(globalScope); const blockScope = new BlockScope(functionScope); expect(globalScope.kind).eq(ScopeKind.GlobalScope); expect(functionScope.kind).eq(ScopeKind.FunctionScope); expect(blockScope.kind).eq(ScopeKind.BlockScope); }); it('findFunctionScope', function () { const globalScope = new GlobalScope(); const functionScope = new FunctionScope(globalScope); const blockScope = new BlockScope(functionScope); functionScope.setFuncName('function1'); const internalFunctionScope = new FunctionScope(blockScope); const internalBlockScope = new BlockScope(internalFunctionScope); internalFunctionScope.setFuncName('function2'); expect(blockScope.findFunctionScope('function1')).eq(functionScope); expect(functionScope.findFunctionScope('function1', false)).eq( undefined, ); expect(functionScope.findFunctionScope('function1')).eq(functionScope); expect(blockScope.findFunctionScope('function2')).eq( internalFunctionScope, ); expect(internalBlockScope.findFunctionScope('function2', false)).eq( undefined, ); expect(internalBlockScope.findFunctionScope('function1')).eq( functionScope, ); }); it('getFunctionFromGlobalScope', function () { const globalScope = new GlobalScope(); globalScope.moduleName = 'moduleA'; mangling([globalScope]); expect(globalScope.startFuncName).eq('moduleA|start'); }); it('getStartFunctionStatementArray', function () { const globalScope = new GlobalScope(); globalScope.addStatement(new Statement(ts.SyntaxKind.IfStatement)); globalScope.addStatement(new Statement(ts.SyntaxKind.ForInStatement)); expect(globalScope.statements.length).eq(2); expect(globalScope.statements[0].statementKind).eq( ts.SyntaxKind.IfStatement, ); expect(globalScope.statements[1].statementKind).eq( ts.SyntaxKind.ForInStatement, ); }); it('getNearestFunctionScope', function () { const globalScope = new GlobalScope(); const funcScope1 = new FunctionScope(globalScope); const blockScope1 = new BlockScope(funcScope1); const funcScope2 = new FunctionScope(funcScope1); const funcScope3 = new FunctionScope(blockScope1); const blockScope2 = new BlockScope(funcScope2); expect(blockScope1.getNearestFunctionScope()).eq(funcScope1); expect(funcScope3.getNearestFunctionScope()).eq(funcScope3); expect(blockScope2.getNearestFunctionScope()).eq(funcScope2); }); it('getRootGloablScope', function () { const globalScope1 = new GlobalScope(); const funcScope1 = new FunctionScope(globalScope1); const blockScope1 = new BlockScope(funcScope1); const globalScope2 = new GlobalScope(); const funcScope2 = new FunctionScope(globalScope2); const blockScope2 = new BlockScope(funcScope2); expect(blockScope1.getRootGloablScope()).eq(globalScope1); expect(blockScope2.getRootGloablScope()).eq(globalScope2); }); it('getTSType', function () { const globalScope = new GlobalScope(); const funcScope = new FunctionScope(globalScope); const funcType = new TSFunction(); funcScope.addType('test', funcType); expect(funcScope.findType('test')).eq(funcType); }); }); ================================================ FILE: tests/unit/statement.test.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import ts from 'typescript'; import 'mocha'; import { expect } from 'chai'; import { EmptyStatement, ForStatement } from '../../src/statement.js'; import { BinaryExpression, IdentifierExpression, NumberLiteralExpression, } from '../../src/expression.js'; describe('testStatement', function () { it('validateStatementRelation', function () { const numberLiteralExpression = new NumberLiteralExpression(3); const identifierExpression = new IdentifierExpression('a'); const binaryExpression = new BinaryExpression( ts.SyntaxKind.MinusToken, identifierExpression, numberLiteralExpression, ); const emptyStatement = new EmptyStatement(); const forStatement = new ForStatement( 'loop0', 'break0', null, binaryExpression, emptyStatement, null, null, ); expect(forStatement.forLoopLabel).eq('loop0'); expect(forStatement.forLoopBlockLabel).eq('break0'); expect(forStatement.forLoopCondtion).eq(binaryExpression); expect(forStatement.forLoopBody).eq(emptyStatement); expect(forStatement.forLoopInitializer).eq(null); expect(forStatement.forLoopIncrementor).eq(null); }); }); ================================================ FILE: tests/unit/type.test.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import 'mocha'; import { expect } from 'chai'; import { Primitive, TSArray, TSClass, TSFunction, Type, TypeKind, TsClassField, FunctionKind, } from '../../src/type.js'; describe('testType', function () { it('validateTypeKind', function () { const baseType = new Type(); const numberType = new Primitive('number'); const stringType = new Primitive('string'); const boolType = new Primitive('boolean'); const anyType = new Primitive('any'); const nullType = new Primitive('null'); const voidType = new Primitive('void'); const classType = new TSClass(); const funcType = new TSFunction(); const numberArrayType = new TSArray(numberType); expect(baseType.kind).eq(TypeKind.UNKNOWN); expect(numberType.kind).eq(TypeKind.NUMBER); expect(stringType.kind).eq(TypeKind.STRING); expect(boolType.kind).eq(TypeKind.BOOLEAN); expect(anyType.kind).eq(TypeKind.ANY); expect(nullType.kind).eq(TypeKind.NULL); expect(voidType.kind).eq(TypeKind.VOID); expect(classType.kind).eq(TypeKind.CLASS); expect(funcType.kind).eq(TypeKind.FUNCTION); expect(numberArrayType.kind).eq(TypeKind.ARRAY); }); it('judgeFunctionType', function () { const funcType = new TSFunction(); const numberType = new Primitive('number'); funcType.addParamType(numberType); funcType.addParamType(numberType); funcType.returnType = numberType; expect(funcType.getParamTypes().length).eq(2); expect(funcType.getParamTypes()[0]).eq(numberType); expect(funcType.getParamTypes()[1]).eq(numberType); expect(funcType.returnType).eq(numberType); expect(funcType.hasRest()).eq(false); }); it('judgeFunctionTypeOptionalParam', function () { const funcType = new TSFunction(); funcType.addIsOptionalParam(true); funcType.addIsOptionalParam(false); expect(funcType.isOptionalParams[0]).eq(true); expect(funcType.isOptionalParams[1]).eq(false); expect(funcType.hasRest()).eq(false); funcType.restParamIdx = 1; expect(funcType.hasRest()).eq(true); expect(funcType.restParamIdx).eq(1); }); it('judgeObjectLiteralType', function () { const objLiteralType = new TSClass(); const objField: TsClassField = { name: 'a', type: new Primitive('number'), }; objLiteralType.addMemberField(objField); const funcType = new TSFunction(); const numberType = new Primitive('number'); funcType.addParamType(numberType); funcType.addParamType(numberType); funcType.returnType = numberType; objLiteralType.addMethod({ name: 'add', type: funcType, }); expect(objLiteralType.getMemberField('a')).eq(objField); expect( objLiteralType.getMethod('add', FunctionKind.DEFAULT).method?.type, ).eq(funcType); expect(objLiteralType.getMethod('add', FunctionKind.DEFAULT).index).eq( 0, ); }); it('judgeClassType', function () { const numberType = new Primitive('number'); const stringType = new Primitive('string'); const baseClassType = new TSClass(); const baseClassField: TsClassField = { name: 'b', type: numberType, }; baseClassType.addMemberField(baseClassField); const baseFuncType = new TSFunction(); baseFuncType.addParamType(numberType); baseFuncType.addParamType(numberType); baseFuncType.returnType = numberType; baseClassType.addMethod({ name: 'add', type: baseFuncType, }); const classType = new TSClass(); const classField1: TsClassField = { name: 'a1', type: numberType, }; const classField2: TsClassField = { name: 'a2', type: stringType, }; classType.addMemberField(classField1); classType.addStaticMemberField(classField2); const funcType = new TSFunction(); funcType.addParamType(numberType); funcType.addParamType(numberType); funcType.returnType = stringType; classType.addMethod({ name: 'add', type: funcType, }); classType.setBase(baseClassType); expect(baseClassType.getMemberField('b')).eq(baseClassField); expect( baseClassType.getMethod('add', FunctionKind.DEFAULT).method?.type, ).eq(baseFuncType); expect(baseClassType.getMethod('add', FunctionKind.DEFAULT).index).eq( 0, ); expect( classType.getMethod('add', FunctionKind.DEFAULT).method?.type, ).eq(funcType); expect(classType.getMethod('add', FunctionKind.DEFAULT).index).eq(0); }); it('judgeArrayType', function () { const numberType = new Primitive('number'); const arrayType1 = new TSArray(numberType); const stringType = new Primitive('string'); const arrayType2 = new TSArray(stringType); const funcType = new TSFunction(); funcType.addParamType(numberType); funcType.addParamType(numberType); funcType.returnType = numberType; const arrayType3 = new TSArray(funcType); const objLiteralType = new TSClass(); const objField: TsClassField = { name: 'a', type: new Primitive('number'), }; objLiteralType.addMemberField(objField); objLiteralType.addMethod({ name: 'add', type: funcType, }); const arrayType4 = new TSArray(objLiteralType); expect(arrayType1.elementType).eq(numberType); expect(arrayType2.elementType).eq(stringType); expect(arrayType3.elementType).eq(funcType); expect(arrayType4.elementType).eq(objLiteralType); }); }); ================================================ FILE: tests/unit/variable.test.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import ts from 'typescript'; import 'mocha'; import { expect } from 'chai'; import { Variable, Parameter, ModifierKind } from '../../src/variable.js'; import { Type } from '../../src/type.js'; describe('testVariable', function () { it('testVariableModifier', function () { const param = new Parameter( 'param', new Type(), [ts.SyntaxKind.ReadonlyKeyword], 0, ); const var1 = new Variable( 'var1', new Type(), [ModifierKind.const, ts.SyntaxKind.DeclareKeyword], 1, ); const var2 = new Variable( 'var2', new Type(), [ ModifierKind.let, ts.SyntaxKind.DeclareKeyword, ts.SyntaxKind.ExportKeyword, ], 2, ); expect(param.isReadOnly()).eq(true); expect(var1.isReadOnly()).eq(false); expect(var1.isConst()).eq(true); expect(var2.isConst()).eq(false); expect(var1.isDeclare()).eq(true); expect(param.isDeclare()).eq(false); expect(var1.isExport()).eq(false); expect(var2.isExport()).eq(true); }); }); ================================================ FILE: tests/utils/test_helper.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import cp from 'child_process'; import fs from 'fs'; import path from 'path'; import os from 'os'; import { ParserContext } from '../../src/frontend.js'; import { WASMGen } from '../../src/backend/binaryen/index.js'; import { setConfig } from '../../config/config_mgr.js'; const doCompile = (filename: string) => { const compiler = new ParserContext(); setConfig({ enableException: true }); if ( process.argv.includes('enableStringRef') || process.env.TEST_STRINGREF ) { setConfig({ enableStringRef: true }); } /* Compile to a temporary file */ try { compiler.parse([filename]); } catch (e) { console.log(e); return null; } const backend = new WASMGen(compiler); backend.codegen({ opt: 3 }); const output = backend.emitBinary(); backend.dispose(); // TODO: should cleanup the temp dir const tempdir = fs.mkdtempSync(path.join(os.tmpdir(), 'ts2wasm-test-')); const tempfile = path.join(tempdir, 'case.wasm'); fs.writeFileSync(tempfile, output, { flag: 'w' }); return tempfile; }; /** * Compile the typescript source file, check if it success * * @param filename the source file to be tested * @returns true if success, false otherwise */ export function testCompile(filename: string): boolean { try { const wasmFile = doCompile(filename); if (!wasmFile) return false; /* TODO: check wasm file */ return fs.existsSync(wasmFile); } catch (e) { console.log(e); return false; } } /** * Compile and execute a typescript source file with wasm VM * * @param filename the source file to be tested, it will be compiled to wasm module and executed by wasm VM * @param func name of the function to be executed, null or '' for global entry function (~start) * @param args arguments passed to the function * @param flags flags passed to wasm VM * @returns stdout of the VM process, throw exception if failed */ export function testWithVM( filename: string, func = '', args: Array = [], flags: string[] = [], ): string { const wasmFile = doCompile(filename); if (!wasmFile) { return ''; } /* Execute with iwasm */ const iwasmArgs = [ '-f', func || '~start', ...flags, wasmFile, ...args.map((a) => a.toString()), ]; return cp.spawnSync('iwasm', iwasmArgs).stdout.toString('utf-8'); } // TODO: implement testWithREPL later // export class testWithREPL { // process : any // constructor(filename: string, flags = [] = []) { // let wasmFile = doCompile(filename); // /* Execute with iwasm */ // let iwasmArgs = ['--repl', ...flags, wasmFile]; // this.process = cp.spawn('iwasm', iwasmArgs); // this.process.stdin.setEncoding('utf-8'); // } // public call(func: string = '', args: Array = []) { // this.process.stdout.pipe(process.stdout); // let cmd = [func, ...args.map((a) => a.toString())].join(' '); // this.process.stdin.write(`${cmd}\n`); // // TODO: get stdout from child process and return to caller // } // } ================================================ FILE: tools/validate/run_module/import_object.js ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ let wasmMemory; function setWasmMemory(value) { wasmMemory = value; } const TAG_PROPERTY = '@tag'; const REF_PROPERTY = '@ref'; const DynType = { DynUnknown: 0, DynUndefined: 1, DynNull: 2, DynObject: 3, DynBoolean: 4, DynNumber: 5, DynString: 6, DynFunction: 7, DynSymbol: 8, DynBigInt: 9, DynExtRefObj: 10, DynExtRefFunc: 11, DynExtRefArray: 12, }; const ExtRefTag = { ExtObj: 0, ExtFunc: 1, ExtArray: 2, }; const getDynTypeTag = (value) => { let res; const tag = value[TAG_PROPERTY]; if (tag === ExtRefTag.ExtObj) { res = DynType.DynExtRefObj; } else if (tag === ExtRefTag.ExtFunc) { res = DynType.DynExtRefFunc; } else if (tag === ExtRefTag.ExtArray) { res = DynType.DynExtRefArray; } else { const type = typeof value; switch (type) { case 'number': res = DynType.DynNumber; break; case 'boolean': res = DynType.DynBoolean; break; case 'string': res = DynType.DynString; break; case 'function': res = DynType.DynFunction; break; case 'symbol': res = DynType.DynSymbol; break; case 'bigint': res = DynType.DynBigInt; break; case 'object': res = DynType.DynObject; break; case 'undefined': res = DynType.DynUndefined; break; default: res = DynType.DynUnknown; break; } } return res; }; const cstringToJsString = (offset) => { let length = 0; let memory = new Uint8Array(wasmMemory.buffer); while (memory[offset + length] !== 0) { length++; } const decoder = new TextDecoder(); const string = decoder.decode(memory.slice(offset, offset + length)); return string; }; const importObject = { libstruct_indirect: { struct_get_indirect_i32: (obj, index) => {}, struct_get_indirect_i64: (obj, index) => {}, struct_get_indirect_f32: (obj, index) => {}, struct_get_indirect_f64: (obj, index) => {}, struct_get_indirect_anyref: (obj, index) => {}, struct_get_indirect_funcref: (obj, index) => {}, struct_set_indirect_i32: (obj, index, value) => {}, struct_set_indirect_i64: (obj, index, value) => {}, struct_set_indirect_f32: (obj, index, value) => {}, struct_set_indirect_f64: (obj, index, value) => {}, struct_set_indirect_anyref: (obj, index, value) => {}, struct_set_indirect_funcref: (obj, index, value) => {}, }, libdyntype: { dyntype_context_init: () => BigInt(0), dyntype_context_destroy: (ctx) => {}, dyntype_get_context: () => BigInt(0), dyntype_new_number: (ctx, value) => { return value; }, dyntype_to_number: (ctx, value) => { if (!importObject.libdyntype.dyntype_is_number(ctx, value)) { throw Error('cast any to number failed: not a number'); } const res = value.valueOf(); return res; }, dyntype_is_number: (ctx, value) => { return typeof value === 'number' || value instanceof Number; }, dyntype_new_boolean: (ctx, value) => { return value; }, dyntype_to_bool: (ctx, value) => { if (!importObject.libdyntype.dyntype_is_bool(ctx, value)) { throw Error('cast any to boolean failed:: not a boolean'); } const res = value.valueOf(); return res; }, dyntype_is_bool: (ctx, value) => { return typeof value === 'boolean' || value instanceof Boolean; }, dyntype_new_string: (ctx, value) => { return value; }, dyntype_to_cstring: (ctx, value) => { const memView = new DataView(wasmMemory.buffer); let res; memView.setInt32(res, value); }, dyntype_free_cstring: (ctx, value) => { // no need in js }, dyntype_is_string: (ctx, value) => { return typeof value === 'string' || value instanceof String; }, dyntype_new_array: (ctx, len) => new Array(len), dyntype_is_array: (ctx, value) => { return Array.isArray(value); }, dyntype_add_elem: (ctx, arr, elem) => { arr.push(elem); }, dyntype_set_elem: (ctx, arr, idx, elem) => { arr[idx] = elem; }, dyntype_get_elem: (ctx, arr, idx) => { return arr[idx]; }, dyntype_typeof: (ctx, value) => { let res; const tag = value[TAG_PROPERTY]; if (tag === ExtRefTag.ExtObj || tag === ExtRefTag.ExtArray) { res = 'object'; } else if (tag === ExtRefTag.ExtFunc) { res = 'function'; } else { res = typeof value; } return res; }, dyntype_typeof1: (ctx, value) => { const res = getDynTypeTag(value); return res; }, dyntype_toString: (ctx, value) => { if (importObject.libdyntype.dyntype_is_extref(ctx, value)) { const type = importObject.libdyntype.dyntype_typeof(ctx, value); if (type == 'object') { return '[object Object]'; } else { return '[wasm Function]'; } } else { return value.toString(); } }, dyntype_type_eq: (ctx, l, r) => { return ( importObject.libdyntype.dyntype_typeof(ctx, l) === importObject.libdyntype.dyntype_typeof(ctx, r) ); }, dyntype_new_object: (ctx) => new Object(), dyntype_set_property: (ctx, obj, prop, value) => { obj[prop] = value; return true; }, dyntype_get_property: (ctx, obj, prop) => { return obj[prop]; }, dyntype_has_property: (ctx, obj, prop) => { return prop in obj; }, dyntype_delete_property: (ctx, obj, prop) => { delete obj[prop]; return true; }, dyntype_is_object: (ctx, obj) => { return typeof obj === 'object'; }, dyntype_is_undefined: (ctx, value) => { return typeof value === 'undefined'; }, dyntype_new_undefined: (ctx) => undefined, dyntype_is_null: () => {}, dyntype_new_null: (ctx) => null, dyntype_get_global: () => {}, dyntype_new_extref: (ctx, value, flag) => { /** TODO: ensure it's truely a external reference */ let ref = {}; ref[REF_PROPERTY] = value; ref[TAG_PROPERTY] = flag; return ref; }, dyntype_is_extref: (ctx, obj) => { /** TODO: ensure it's truely a external reference */ const tag = obj[TAG_PROPERTY]; if ( tag === ExtRefTag.ExtObj || tag === ExtRefTag.ExtFunc || tag === ExtRefTag.ExtArray ) { return true; } return false; }, dyntype_to_extref: (ctx, obj) => { if (!importObject.libdyntype.dyntype_is_extref(ctx, obj)) { throw Error('cast any to extref failed: not an extref'); } let res = obj[REF_PROPERTY]; return res; }, dyntype_get_prototype: (ctx, obj) => { return Object.getPrototypeOf(obj); }, dyntype_set_prototype: (ctx, obj, proto) => { Object.setPrototypeOf(obj, proto); }, dyntype_instanceof: () => {}, dyntype_to_string: () => {}, dyntype_is_falsy: () => {}, dyntype_cmp: () => {}, dyntype_new_object_with_class: (ctx, name, args_array) => { let ctor = undefined; const str_value = cstringToJsString(name); if (typeof window === 'undefined') { ctor = global[str_value]; } else { ctor = window[str_value]; } return new ctor(...args_array); }, dyntype_invoke: (ctx, name, obj, args_array) => { let res = undefined; const str_value = cstringToJsString(name); if (str_value != '') { res = obj[str_value](...args_array); } else { res = obj(...args_array); } return res; }, dyntype_get_keys: (ctx, obj) => { return Object.keys(obj); } }, env: { Console_log: (obj) => { /** TODO: cant log reference type variable */ console.log(obj); }, Console_constructor: (obj) => {}, strcmp(a, b) { let lhs = cstringToJsString(a); let rhs = cstringToJsString(b); return lhs.localeCompare(rhs); }, setTimeout: (obj) => {}, clearTimeout: (obj) => {}, malloc: (size)=>{}, free: (size)=>{}, array_push_generic: (ctx, obj, elem) => {}, array_pop_f64: (ctx, obj) => {}, array_pop_i64: (ctx, obj) => {}, array_pop_f32: (ctx, obj) => {}, array_pop_i32: (ctx, obj) => {}, array_pop_anyref: (ctx, obj) => {}, array_concat_generic: (ctx, obj1, obj2) => {}, array_reverse_generic: (ctx, obj) => {}, array_shift_f64: (ctx, obj) => {}, array_shift_i64: (ctx, obj) => {}, array_shift_f32: (ctx, obj) => {}, array_shift_i32: (ctx, obj) => {}, array_shift_anyref: (ctx, obj) => {}, array_slice_generic: () => {}, array_join_f64: () => {}, array_join_i64: () => {}, array_join_f32: () => {}, array_join_i32: () => {}, array_join_anyref: () => {}, array_find_generic: () => {}, array_sort_generic: () => {}, array_splice_generic: () => {}, array_unshift_generic: () => {}, array_indexOf_f64: () => {}, array_indexOf_i64: () => {}, array_indexOf_f32: () => {}, array_indexOf_i32: () => {}, array_indexOf_anyref: () => {}, array_lastIndexOf_f64: () => {}, array_lastIndexOf_i64: () => {}, array_lastIndexOf_f32: () => {}, array_lastIndexOf_i32: () => {}, array_lastIndexOf_anyref: () => {}, array_every_generic: () => {}, array_some_generic: () => {}, array_forEach_generic: () => {}, array_map_generic: () => {}, array_filter_generic: () => {}, array_reduce_f64: () => {}, array_reduce_i64: () => {}, array_reduce_f32: () => {}, array_reduce_i32: () => {}, array_reduce_anyref: () => {}, array_reduceRight_f64: () => {}, array_reduceRight_i64: () => {}, array_reduceRight_f32: () => {}, array_reduceRight_i32: () => {}, array_reduceRight_anyref: () => {}, array_find_f64: () => {}, array_find_i64: () => {}, array_find_f32: () => {}, array_find_i32: () => {}, array_find_anyref: () => {}, array_findIndex_generic: () => {}, array_fill_f64: () => {}, array_fill_i64: () => {}, array_fill_f32: () => {}, array_fill_i32: () => {}, array_fill_anyref: () => {}, array_copyWithin_generic: () => {}, array_includes_f64: () => {}, array_includes_i64: () => {}, array_includes_f32: () => {}, array_includes_i32: () => {}, array_includes_anyref: () => {}, }, }; export { importObject, setWasmMemory }; ================================================ FILE: tools/validate/run_module/readme.md ================================================ # Run generated WASM module This document describes how to execute WASM module on node.js and on chrome. > Note: Wasmnizer-ts follows the latest WasmGC spec, which requires `V8 v11.9+`, but the latest nodejs (v21.5.0) is using `V8 11.8.172.17`, so currently the generated WASM module can't execute on any nodejs releases. > If you do want to try on nodejs, you can reset to commit `94cf9929421d47a9976fa6edf74b25ef2a00ee12` to build the compiler, which is compatible to older V8 versions. ## Run module on node ### Prerequisites - node.js version 20.0.0 or higher to enable support for `stringref` feature, node.js version 20.0 or higher is necessary. - required flags in Node.js - `--experimental-wasm-gc`: This flag is required to enable support for the WASM GC feature. - `--experimental-wasm-stringref`: This flag is needed to enable support for the `stringref` feature. ### How to Run To run your WebAssembly file, use the following command: ```shell node --experimental-wasm-gc --experimental-wasm-stringref run_module.js /path/to/your.wasm ``` The parameters to pass to the exported WASM function should followed by the module path, if needed. You can also use the following options if needed: - `-f`: specify the exported WASM function you want to execute in Node.js. - `-s`: specify to execute the `_start` WASM function to initialize global variables if necessary. ### Example Here is an example. The `example.wasm` file is generated from the following TypeScript source code: ```typescript export function foo(a: number, b: string) { if (b === 'Hello World') { return a; } return a + 1; } ``` The following command demonstrates how to run the exported function `foo`: ```shell node --experimental-wasm-gc --experimental-wasm-stringref run_module.js -f foo example.wasm 1 'Hello World' ``` it will output `1`. ## Run module on chrome ### Prerequisites - Set chrome flags by `chrome://flags`, should set these flags as enabled: - Experimental WebAssembly - WebAssembly Garbage Collection - WebAssembly Stringref ### How to Run Start a server, open the `run_module_on_chrome.html` on chrome, fill in with the wasm path, the wasm function name, and arguments(must be separated by commas), then click `submit` button, and the result will be print on the page. ================================================ FILE: tools/validate/run_module/run_module_on_chrome.html ================================================ run wasm module

================================================ FILE: tools/validate/run_module/run_module_on_chrome.js ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import { importObject, setWasmMemory } from './import_object.js'; export function run_wasm_module(filePath, funcName, warmupTimes, runTarget, ...funcArgs) { const parts = filePath.split("."); const extension = parts[parts.length - 1]; if (runTarget === 'js') { if (extension !== 'js') { const resultElement = document.getElementById('result'); resultElement.innerHTML = `Error: filePath must end with ".js`; } fetch(filePath) .then(response => response.text()) .then(script => { if (warmupTimes) { for (let i = 0; i < parseInt(warmupTimes); i++) { eval(script); } } const start_time = performance.now(); let res = eval(script); if (funcName) { res = window[funcName](...funcArgs); } const end_time = performance.now(); if (typeof res !== 'object' || res === null) { const resultElement = document.getElementById('result'); resultElement.innerHTML = `The result is: ${res}`; } const timeElement = document.getElementById('time'); timeElement.innerHTML = `Execution time is: ${end_time - start_time}`; }); } else if (runTarget === 'wasm') { if (extension !== 'wasm') { const resultElement = document.getElementById('result'); resultElement.innerHTML = `Error: filePath must end with ".wasm`; } fetch(filePath) .then((response) => response.arrayBuffer()) .then((bytes) => WebAssembly.instantiate(bytes, importObject)) .then((results) => { const exports = results.instance.exports; setWasmMemory(exports.default); const startFunc = exports._entry; const exportedFunc = exports[funcName]; if (warmupTimes) { for (let i = 0; i < parseInt(warmupTimes); i++) { startFunc(); exportedFunc(...funcArgs); } } const start_time = performance.now(); startFunc(); const res = exportedFunc(...funcArgs); const end_time = performance.now(); if (typeof res !== 'object' || res === null) { const resultElement = document.getElementById('result'); resultElement.innerHTML = `The result is: ${res}`; } const timeElement = document.getElementById('time'); timeElement.innerHTML = `Execution time is: ${end_time - start_time}`; }); } } ================================================ FILE: tools/validate/run_module/run_module_on_node.js ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import fs from 'fs'; import minimist from 'minimist'; import { importObject, setWasmMemory } from './import_object.js'; function showHelp() { console.log(`Options: -f: specify the exported WASM function -s: call the _start function -h: show help message\n`); } const cliArgs = minimist(process.argv.slice(2)); if (cliArgs.help || cliArgs.h) { showHelp(); process.exit(0); } if (cliArgs._.length === 0) { console.log('WASM module path is required'); process.exit(1); } const modulePath = cliArgs._[0]; const funcName = cliArgs.f; const needCallStart = cliArgs.s; const args = cliArgs._.slice(1); const wasmBuffer = fs.readFileSync(modulePath); WebAssembly.instantiate(wasmBuffer, importObject).then((wasmModule) => { const exports = wasmModule.instance.exports; setWasmMemory(exports.default); const _start = exports._start; if (needCallStart || !funcName) { _start(); } if (!funcName) { return; } const exportedFunc = exports[funcName]; const res = exportedFunc(...args); console.log(res); }); ================================================ FILE: tools/validate/wamr/.gitignore ================================================ test.log wasm_modules/ ================================================ FILE: tools/validate/wamr/README.md ================================================ # validate execution on WAMR This folder contains code to compile all the sample code and execute on WAMR. ## Run Please ensure you have built the `iwasm_gc` in default directory, see this [document](../../../runtime-library/README.md) ``` bash npm start ``` The script will print the pass rate, and the failed cases will be recorded into the `test.log` file. ### Typical errors - `Running [xxx] get invalid return code: xxx` - This error means the `iwasm_gc` returned with non-zero code - if code is null, it means the runtime process crashed - `Running [xxx] get unexpected output` - This means the actual output of the wasm module is not expected text ### How it works? 1. Compile the ts source code to wasm module 2. Run the wasm module on WAMR with given arguments in `validation.json` 3. Compare the output with the expected result > Note: The failed cases doesn't always mean there are bugs in the compiler or runtime, many known reasons may cause the cases to fail: > > 1. Some cases require non-primitive arguments (e.g. struct, any) but currently the runtime only receives `number` type arguments through cli > 2. Some cases require some imported functions/globals, but the runtime didn't provide them > 3. Some cases return complex values (e.g. struct, array, any), the runtime can't print detailed information, so the output is not accurate ## validation.json format `validation.json` is a json file to record the cases to be tested, the format is: ``` json [ { "module": "any_binary_add", "entries": [ { "name": "addAnyAny", "args": [], "result": "3:f64" }, { "name": "addNumberAnyInBinaryExpr", "args": [], "result": "2:f64" }, { "name": "addNumberAnyInMulExpr", "args": [], "result": "9:f64" } ] }, { // ... } ] ``` - `module` is the name of the ts file to be tested - `entries` is an array of functions to be tested - `name` is the name of the exported function - `args` is an array of arguments, currently only support number - `result` is the expected output text ## Auto generate validation.json > This is for develop usage, don't run this script unless you know what will happen. It's tedious to write the `validation.json` file manually, so we provide a script to generate it automatically. ``` bash npm run gen_item ``` ### How it works? 1. The script use ts2wasm frontend to parse the ts source code, and extract all export functions, and get the parameter count of the function 2. For each function, it will randomly generate some argument and call it with `ts-node` 3. The generated args, and the executed result will be recorded into the `validation.json` file > Note: This script will overwrite the `validation.json` file, so please backup it before running this script. > Note: Some functions may not be able to run on `ts-node`, so the script will remain the `result` empty. Also some text may not be very accurate, you need to fill/correct it manually. ================================================ FILE: tools/validate/wamr/create_validation_items.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import fs from 'fs'; import path from 'path'; import { ParserContext } from '../../../src/frontend.js'; import { fileURLToPath } from 'url'; import { FunctionScope } from '../../../src/scope.js'; const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url)); const SAMPLES_DIR = path.join(SCRIPT_DIR, '../../../tests/samples'); const validationData: any = []; let tsFiles = fs.readdirSync(SAMPLES_DIR); tsFiles = tsFiles.filter((f) => f !== 'sample_cases.test.ts'); console.log('scanning source files ...'); for (const file of tsFiles) { const currentTsFile = `${SAMPLES_DIR}/${file}`; if (fs.lstatSync(currentTsFile).isDirectory()) continue; try { const parserCtx = new ParserContext(); console.log(`analyzing [${file}] ...`); try { parserCtx.parse([currentTsFile]); } catch (e) { console.error(`Parsing [${file}] failed, skip it`); continue; } const entryScope = parserCtx.globalScopes[parserCtx.globalScopes.length - 1]; const exportFuncs = entryScope.children.filter((s) => { return s instanceof FunctionScope && s.isExport(); }); const newItem: any = { module: path.parse(file).name, entries: [], }; for (const f of exportFuncs) { let result = ''; const args = (f as FunctionScope).paramArray .filter((p) => p.varName !== '@context') .map((p) => Math.floor(Math.random() * 40)); try { result = (await import(currentTsFile))[f.getName()](...args); if (typeof result === 'number') { let value = `${result}`; if (!Number.isInteger(result)) { value = (result as number).toFixed(6); } result = `${value}:f64`; } else if (typeof result === 'boolean') { result = `${result ? '0x1' : '0x0'}:i32`; } else if ((result as any) instanceof Array) { result = `ref.array`; } else { result = `ref.struct`; } } catch (e) { console.error( `Failed to execute ${f.getName()}(${args}) by ts-node:`, ); console.error(`${e}`); result = 'need_manual_fill'; } newItem.entries.push({ name: f.getName(), args: args, result: result, }); } validationData.push(newItem); } catch (e) { console.error(`Analyzing [${file}] failed`); throw e; } } fs.writeFileSync( path.join(SCRIPT_DIR, 'validation.json'), JSON.stringify(validationData, null, 4), ); process.exit(0); ================================================ FILE: tools/validate/wamr/index.ts ================================================ /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ import fs from 'fs'; import path from 'path'; import cp from 'child_process'; import { ParserContext } from '../../../src/frontend.js'; import { fileURLToPath } from 'url'; import { WASMGen } from '../../../src/backend/binaryen/index.js'; import validationItems from './validation.json' assert { type: 'json' }; import { setConfig } from '../../../config/config_mgr.js'; let IGNORE_CASES = [ /* Need manual validation */ 'any_box_null:boxNull', 'any_box_obj:boxEmptyObj', 'any_box_string:boxStringWithVarStmt', 'any_box_string:boxStringWithBinaryExpr', 'any_box_undefind:boxUndefined', 'cast_any_to_static:castAnyBackToUndefined', 'prototype:returnPrototypeObject', /* ignored in compilation test */ 'complexType_case1:complexTypeTest', 'complexType_case2:cpxCase2Func3', 'global_generics_function:test', 'inner_generics_function:test', 'namespace_generics_function:test', 'ignore_parameter_in_variable.ts', /* require host API */ 'declare_class:classDecl', 'declare_func:assignDeclareFuncToVar', /* workaround: generic type */ // 'import_type:validateTypeArguments', /* function not exported */ 'export_namespace:bFunc', /* exception handling not support yet */ 'exception_catch_error.ts', 'exception_custom_error.ts', 'exception_throw_error.ts', 'exception_try_structure.ts', 'promise_throw:promiseThrowError', 'promise_throw:promiseCatchInCB', 'promise_throw:promiseNotCatchInCB', 'rec_types:recursiveType1', 'rec_types:recursiveType2', 'rec_types:defaultFuncUseRecType', ]; if (process.env.SIMPLE_LIBDYNTYPE === '1') { console.log('Testing with simple libdyntype implementation'); const simple_libdyntype_ignores: string[] = [ 'any_func_call:anyFuncCallInMap' /* Map not supported */, 'any_func_call:anyFuncCallWithCast' /* Map not supported */, 'any_func_call:anyFuncCallWithNoCast' /* Map not supported */, 'array_foreach:array_foreach_closure' /* Map not supported */, 'any_box_obj:boxObjWithProps' /* key order changed, but result is correct */, 'builtin_string' /* string method is dynamic invoke */, 'builtin_console:specialNum' /* print different, but result is correct */, 'promise_chain' /* Promise not supported */, 'promise_constructor' /* Promise not supported */, 'promise_immediate' /* Promise not supported */, 'prototype' /* prototype not supported */, 'string_type:unicode' /* string encoding not supported */, 'fallback_quickjs' /* Map not supported */, 'fallback_quickjs_JSON' /* JSON not supported */, 'fallback_quickjs_Date' /* Date methods not supported */, 'toString:toStringTest' /* dynamic array toString not support */, 'map_callback' /* Map not supported */, 'for_in:dynamic_obj' /* key order changed, but result is correct */, 'for_of' /* Map and Set not supported */, 'wasmType_basic:wasmTypeAssign' /* float precise different */, 'wasmType_basic:toF32Value' /* float precise different */, 'wasmType_basic:toF64Value' /* float precise different */, 'wasmType_in_otherType:wasmTypeAs' /* float precise different */, 'wasmType_in_otherType:wasmTypeInArray' /* float precise different */, ]; IGNORE_CASES = IGNORE_CASES.concat(simple_libdyntype_ignores); } if (process.env.AOT) { console.log('Testing with AOT'); if (process.env.TARGET_ARCH) { console.log(`AOT Target arch: ${process.env.TARGET_ARCH}`); } } else { console.log('Testing with interpreter'); } setConfig({ enableStringRef: true, opt: 0 }); const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url)); const SAMPLES_DIR = path.join(SCRIPT_DIR, '../../../tests/samples'); const COMPILE_DIR = path.join(SCRIPT_DIR, 'wasm_modules'); const IWASM_GC_DIR = path.join( SCRIPT_DIR, '../../../runtime-library/build/iwasm_gc', ); const BUILD_SCRIPT_DIR = path.join( SCRIPT_DIR, '../../../runtime-library/build.sh', ); const TEST_LOG_FILE = path.join(SCRIPT_DIR, 'test.log'); const WAMRC_DIR = path.join( SCRIPT_DIR, '../../../runtime-library/deps/wamr-gc/wamr-compiler/build/wamrc', ); if (!fs.existsSync(IWASM_GC_DIR)) { console.error('iwasm_gc not found, build it firstly'); const result = cp.execFileSync(BUILD_SCRIPT_DIR, { stdio: 'inherit' }); } fs.writeFileSync( TEST_LOG_FILE, `Start validation on WAMR ... ${new Date()}\n\n`, ); if (fs.existsSync(COMPILE_DIR)) { fs.rmSync(COMPILE_DIR, { recursive: true }); } fs.mkdirSync(COMPILE_DIR); let totalCases = 0; let totalFail = 0; let totalCompilationFail = 0; let totalNeedManualValidation = 0; let totalSkippedCases = 0; validationItems.forEach((item) => { const sourceFile = `${SAMPLES_DIR}/${item.module}.ts`; const outputFile = `${COMPILE_DIR}/${item.module}.wasm`; const outputAoTFile = `${COMPILE_DIR}/${item.module}.aot`; const moduleEntries = item.entries.length; totalCases += moduleEntries; let compilationSuccess = false; try { const parserCtx = new ParserContext(); console.log(`Validating [${item.module}] ...`); parserCtx.parse([sourceFile]); const backend = new WASMGen(parserCtx); backend.codegen(); const wasmBuffer = backend.emitBinary(); fs.writeFileSync(outputFile, wasmBuffer); backend.dispose(); if (process.env.AOT) { const wamrcArgs = ['--enable-gc', '-o', outputAoTFile, outputFile]; if (process.env.TARGET_ARCH === 'X86_32') { wamrcArgs.unshift('--target=i386'); } const result = cp.spawnSync(WAMRC_DIR, wamrcArgs); if (result.status !== 0) { console.error(result.stdout!.toString()); console.error(result.error!.toString()); throw new Error(`Compiling [${item.module}] to AoT failed`); } } compilationSuccess = true; } catch { console.error(`Compiling [${item.module}] failed`); } item.entries.forEach((entry) => { const itemName = `${item.module}:${entry.name}`; if ( IGNORE_CASES.includes(item.module) || IGNORE_CASES.includes(itemName) ) { fs.appendFileSync( TEST_LOG_FILE, `===================================================================================\n`, ); fs.appendFileSync(TEST_LOG_FILE, `[${itemName}] skipped\n`); fs.appendFileSync( TEST_LOG_FILE, `-----------------------------------------------------------------------------------\n`, ); fs.appendFileSync( TEST_LOG_FILE, `source code: \n\t${sourceFile}\n`, ); fs.appendFileSync( TEST_LOG_FILE, `===================================================================================\n\n\n`, ); totalSkippedCases++; return; } if (!compilationSuccess) { totalCompilationFail++; fs.appendFileSync( TEST_LOG_FILE, `===================================================================================\n`, ); fs.appendFileSync( TEST_LOG_FILE, `Running [${itemName}] failed due to compilation error\n`, ); fs.appendFileSync( TEST_LOG_FILE, `-----------------------------------------------------------------------------------\n`, ); fs.appendFileSync( TEST_LOG_FILE, `source code: \n\t${sourceFile}\n`, ); fs.appendFileSync( TEST_LOG_FILE, `===================================================================================\n\n\n`, ); totalFail++; return; } const iwasmArgs = [ '-f', entry.name, process.env.AOT ? outputAoTFile : outputFile, ...entry.args.map((a: any) => a.toString()), ]; const expectRet = (entry as any).ret || 0; const result = cp.spawnSync(IWASM_GC_DIR, iwasmArgs); const cmdStr = `${IWASM_GC_DIR} ${iwasmArgs.join(' ')}`; if (result.status !== expectRet) { fs.appendFileSync( TEST_LOG_FILE, `===================================================================================\n`, ); fs.appendFileSync( TEST_LOG_FILE, `Running [${itemName}] get invalid return code: ${result.status}\n`, ); fs.appendFileSync(TEST_LOG_FILE, `stdout:\n`); fs.appendFileSync(TEST_LOG_FILE, result.stdout.toString('utf-8')); fs.appendFileSync(TEST_LOG_FILE, `stderr:\n`); fs.appendFileSync(TEST_LOG_FILE, result.stderr.toString('utf-8')); fs.appendFileSync( TEST_LOG_FILE, `-----------------------------------------------------------------------------------\n`, ); fs.appendFileSync( TEST_LOG_FILE, `source code: \n\t${sourceFile}\n`, ); fs.appendFileSync( TEST_LOG_FILE, `wasm module: \n\t${outputFile}\n`, ); fs.appendFileSync(TEST_LOG_FILE, `reproduce cmd: \n\t${cmdStr}\n`); fs.appendFileSync( TEST_LOG_FILE, `===================================================================================\n\n\n`, ); totalFail++; } else { const expected = entry.result; const executOutput = result.stdout.toString('utf-8').trim(); if (executOutput !== expected) { fs.appendFileSync( TEST_LOG_FILE, `===================================================================================\n`, ); fs.appendFileSync( TEST_LOG_FILE, `Running [${itemName}] get unexpected output\n`, ); fs.appendFileSync(TEST_LOG_FILE, `\tExpected: ${expected}\n`); fs.appendFileSync(TEST_LOG_FILE, `\tGot: ${executOutput}\n`); fs.appendFileSync( TEST_LOG_FILE, `-----------------------------------------------------------------------------------\n`, ); fs.appendFileSync( TEST_LOG_FILE, `source code: \n\t${sourceFile}\n`, ); fs.appendFileSync( TEST_LOG_FILE, `wasm module: \n\t${outputFile}\n`, ); fs.appendFileSync( TEST_LOG_FILE, `reproduce cmd: \n\t${cmdStr}\n`, ); fs.appendFileSync( TEST_LOG_FILE, `===================================================================================\n\n\n`, ); totalFail++; if (executOutput.indexOf('ref')) { totalNeedManualValidation++; } } } }); }); console.log( `${totalCases - totalFail - totalSkippedCases} / ${ totalCases - totalSkippedCases } passed!`, ); console.log(`-------------------------------------------------------------`); console.log(`In the ${totalFail} failed cases:`); console.log( ` * ${totalCompilationFail} cases failed due to compilation error`, ); console.log( ` * ${totalNeedManualValidation} cases need manual validation due to complex return type`, ); console.log(`-------------------------------------------------------------`); console.log(` * ${totalSkippedCases} cases skipped`); if (totalFail > 0) { process.exit(1); } process.exit(0); ================================================ FILE: tools/validate/wamr/package.json ================================================ { "name": "wamr-validation", "version": "1.0.0", "description": "This folder contains code to compile all the sample code and execute on WAMR.", "type": "module", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "cd .. && npm install && cd - && ts-node-esm index.ts", "gen_item": "ts-node-esm create_validation_items.ts" }, "author": "", "license": "ISC" } ================================================ FILE: tools/validate/wamr/tsconfig.json ================================================ { "compilerOptions": { /* Visit https://aka.ms/tsconfig to read more about this file */ /* Projects */ // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ /* Language and Environment */ "target": "es2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "jsx": "preserve", /* Specify what JSX code is generated. */ // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ /* Modules */ "module": "ESNext", /* Specify what module code is generated. */ // "rootDir": "./", /* Specify the root folder within your source files. */ "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ // "types": [], /* Specify type package names to be included without being referenced in a source file. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ "resolveJsonModule": true, /* Enable importing .json files. */ // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ /* JavaScript Support */ // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ /* Emit */ // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ // "declarationMap": true, /* Create sourcemaps for d.ts files. */ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ // "outDir": "./", /* Specify an output folder for all emitted files. */ // "removeComments": true, /* Disable emitting comments. */ // "noEmit": true, /* Disable emitting files from a compilation. */ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ // "newLine": "crlf", /* Set the newline character for emitting files. */ // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ /* Interop Constraints */ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ /* Type Checking */ "strict": true, /* Enable all strict type-checking options. */ // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ "skipLibCheck": true /* Skip type checking all .d.ts files. */ } } ================================================ FILE: tools/validate/wamr/validation.json ================================================ [ { "module": "any_binary_operation", "entries": [ { "name": "addAnyAny", "args": [], "result": "3:f64" }, { "name": "addNumberAnyInBinaryExpr", "args": [], "result": "200:f64" }, { "name": "addNumberAnyInMulExpr", "args": [], "result": "9:f64" }, { "name": "anyCmp", "args": [], "result": "false\ntrue\ntrue\nfalse\ntrue\nfalse\nfalse\ntrue\ntrue\nfalse\nfalse" }, { "name": "anyNullUndefinedCmp", "args": [], "result": "true\nfalse\ntrue\ntrue\nfalse\ntrue\nfalse" }, { "name": "anyMinusAny", "args": [], "result": "5" }, { "name": "anyDivideAny", "args": [], "result": "2" }, { "name": "anyMultiplyAny", "args": [], "result": "50" }, { "name": "anyModAny", "args": [], "result": "0" }, { "name": "addAnyInBinaryExpr", "args": [], "result": "str11astr33" }, { "name": "anyCmpNum", "args": [], "result": "Greater than 9\nLess than 11" } ] }, { "module": "any_box_any", "entries": [ { "name": "boxAny", "args": [], "result": "1:f64" } ] }, { "module": "any_box_array", "entries": [ { "name": "boxEmptyArr", "args": [], "result": "[]" }, { "name": "setArrElem", "args": [], "result": "[100]" }, { "name": "getArrElem", "args": [], "result": "10" }, { "name": "boxNestedArr", "args": [], "result": "2" } ] }, { "module": "any_box_boolean", "entries": [ { "name": "boxBooleanWithVarStmt", "args": [], "result": "0x1:i32" }, { "name": "boxBooleanWithBinaryExpr", "args": [], "result": "0x1:i32" } ] }, { "module": "any_box_interface", "entries": [ { "name": "boxInterface", "args": [], "result": "0x1:i32" } ] }, { "module": "any_box_null", "entries": [ { "name": "boxNull", "args": [], "result": "ref.struct" } ] }, { "module": "any_box_number", "entries": [ { "name": "boxNumberWithVarStmt", "args": [], "result": "1:f64" }, { "name": "boxNumberWithBinaryExpr", "args": [], "result": "1:f64" } ] }, { "module": "any_box_obj", "entries": [ { "name": "boxEmptyObj", "args": [], "result": "ref.struct" }, { "name": "boxObjWithNumberProp", "args": [], "result": "1:f64" }, { "name": "boxObjWithBooleanProp", "args": [], "result": "0x1:i32" }, { "name": "boxNestedObj", "args": [], "result": "1:f64" }, { "name": "anyPointToObj", "args": [], "result": "123" }, { "name": "boxObjWithProps", "args": [], "result": "tag: a\nx: 1\nobject\nstring: 3\n4" } ] }, { "module": "any_box_obj_as_return_value", "entries": [ { "name": "getObjPropFromReturnValue", "args": [], "result": "1" } ] }, { "module": "any_box_string", "entries": [ { "name": "boxStringWithVarStmt", "args": [], "result": "ref.struct" }, { "name": "boxStringWithBinaryExpr", "args": [], "result": "ref.struct" }, { "name": "stringPlusAnyString", "args": [], "result": "string2string1\nstring3string2string1" } ] }, { "module": "any_box_undefind", "entries": [ { "name": "boxUndefined", "args": [], "result": "ref.struct" } ] }, { "module": "any_func_call", "entries": [ { "name": "anyFuncCallWithNumber", "args": [], "result": "20\n110" }, { "name": "anyFuncCallWithBoolean", "args": [], "result": "true\n11" }, { "name": "anyFuncCallWithString", "args": [], "result": "hi\nhello, world" }, { "name": "anyFuncCallWithAny", "args": [], "result": "100\nworld" }, { "name": "anyFuncCallWithFunc", "args": [], "result": "8" }, { "name": "anyFuncCallWithClass", "args": [], "result": "3\ntrue\n1" }, { "name": "anyFuncCallWithObj_static", "args": [], "result": "3\ntrue\n1" }, { "name": "anyFuncCallWithInfc_class", "args": [], "result": "3\ntrue\n1" }, { "name": "anyFuncCallWithInfc_obj", "args": [], "result": "3\ntrue\n1" }, { "name": "anyFuncCallWithArray_static", "args": [], "result": "9\n10\n2" }, { "name": "anyFuncCallWithArray_extref", "args": [], "result": "9\n10\n2" }, { "name": "anyFuncCallInMap", "args": [], "result": "10" }, { "name": "anyFuncCallWithCast", "args": [], "result": "Page class\n1" }, { "name": "anyFuncCallWithNoCast", "args": [], "result": "Page class\n1" }, { "name": "unionFuncCall", "args": [], "result": "1" } ] }, { "module": "any_nested_literal_value", "entries": [ { "name": "arrayLiteralInObjLiteral", "args": [], "result": "100" }, { "name": "objLiteralInArrayLiteral", "args": [], "result": "world" } ] }, { "module": "any_obj_prop_get", "entries": [ { "name": "getProp", "args": [], "result": "4:f64" } ] }, { "module": "any_obj_prop_set", "entries": [ { "name": "setUnExistProp", "args": [], "result": "4:f64" }, { "name": "setExistProp", "args": [], "result": "2:f64" } ] }, { "module": "any_as_string", "entries": [ { "name": "any_as_string", "args": [], "result": "hello world" } ] }, { "module": "array_contain_closure", "entries": [ { "name": "containClosure", "args": [], "result": "11:f64" } ] }, { "module": "array_contain_func", "entries": [ { "name": "containFunc", "args": [], "result": "20:f64" } ] }, { "module": "array_elem_get", "entries": [ { "name": "arrayTest6", "args": [], "result": "1:f64" } ] }, { "module": "array_elem_set", "entries": [ { "name": "setElem", "args": [], "result": "5:f64" } ] }, { "module": "array_nested_array", "entries": [ { "name": "nestedArray", "args": [], "result": "100:f64" } ] }, { "module": "array_nested_literal", "entries": [ { "name": "nestedLiteral", "args": [], "result": "3:f64" } ] }, { "module": "array_nested_literal_array", "entries": [ { "name": "nestedLiteralArrayInOneLayer", "args": [], "result": "ref.struct" }, { "name": "nestedLiteralArrayInMulLayer", "args": [], "result": "ref.struct" } ] }, { "module": "array_new_array", "entries": [] }, { "module": "array_new_array_number", "entries": [ { "name": "newArrayNumber", "args": [], "result": "3:f64" }, { "name": "newArrayNumberWithParam", "args": [ 1 ], "result": "1:f64" } ] }, { "module": "array_new_array_string", "entries": [ { "name": "newArrayString", "args": [], "result": "ref.struct" } ] }, { "module": "array_new_array", "entries": [ { "name": "implElemType", "args": [], "result": "12:f64" } ] }, { "module": "array_new_literal_any", "entries": [ { "name": "newLiteralExplicitAny", "args": [], "result": "1:f64" }, { "name": "newLiteralNonExplicitAny", "args": [], "result": "1:f64" } ] }, { "module": "array_new_literal_boolean", "entries": [ { "name": "newLiteralBoolean", "args": [], "result": "0x1:i32" } ] }, { "module": "array_new_literal_number", "entries": [ { "name": "newLiteralNumberWithLiteralType", "args": [], "result": "3:f64" }, { "name": "newLiteralNumberWithArrayType", "args": [], "result": "3:f64" }, { "name": "newLiteralNumberWithoutInit", "args": [], "result": "ref.struct" } ] }, { "module": "array_new_literal_string", "entries": [ { "name": "newLiteralString", "args": [], "result": "ref.struct" } ] }, { "module": "array_push", "entries": [ { "name": "array_push_number", "args": [], "result": "4:f64" }, { "name": "array_push_number_with_empty", "args": [], "result": "10:f64" }, { "name": "array_class2", "args": [], "result": "123\n123" }, { "name": "array_push_boolean", "args": [], "result": "4:f64" }, { "name": "array_push_string", "args": [], "result": "4:f64" }, { "name": "array_push_class", "args": [], "result": "5:f64" }, { "name": "array_push_interface", "args": [], "result": "3:f64" }, { "name": "array_push_number_array", "args": [], "result": "4:f64" }, { "name": "array_push_string_array", "args": [], "result": "123\n3:f64" } ] }, { "module": "array_unshift", "entries": [ { "name": "array_unshift_number", "args": [], "result": "4:f64" }, { "name": "array_unshift_boolean", "args": [], "result": "4:f64" }, { "name": "array_unshift_string", "args": [], "result": "4:f64" }, { "name": "array_unshift_class", "args": [], "result": "5:f64" }, { "name": "array_unshift_interface", "args": [], "result": "3:f64" }, { "name": "array_unshift_number_array", "args": [], "result": "4:f64" }, { "name": "array_unshift_string_array", "args": [], "result": "wasm\n3:f64" } ] }, { "module": "array_filter", "entries": [ { "name": "array_filter_number", "args": [], "result": "4:f64" }, { "name": "array_filter_boolean", "args": [], "result": "3:f64" }, { "name": "array_filter_string", "args": [], "result": "2:f64" }, { "name": "array_filter_class", "args": [], "result": "1:f64" }, { "name": "array_filter_interface", "args": [], "result": "2:f64" }, { "name": "array_filter_number_array", "args": [], "result": "3:f64" }, { "name": "array_filter_string_array", "args": [], "result": "h\n1:f64" }, { "name": "push_in_callback", "args": [], "result": "4:f64" }, { "name": "modify_in_callback", "args": [], "result": "0:f64" }, { "name": "nested_filter", "args": [], "result": "1:f64" }, { "name": "callback_use_closure_var", "args": [], "result": "4:f64" } ] }, { "module": "array_pop", "entries": [ { "name": "array_pop_number", "args": [], "result": "2:f64" }, { "name": "array_pop_boolean", "args": [], "result": "2:f64" }, { "name": "array_pop_string", "args": [], "result": "1:f64" }, { "name": "array_pop_class", "args": [], "result": "1:f64" }, { "name": "array_pop_interface", "args": [], "result": "1:f64" }, { "name": "array_pop_number_array", "args": [], "result": "5:f64" }, { "name": "array_pop_string_array", "args": [], "result": "123\n2:f64" } ] }, { "module": "array_map", "entries": [ { "name": "array_map_number", "args": [], "result": "223:f64" }, { "name": "array_map_boolean", "args": [], "result": "3:f64" }, { "name": "array_map_string", "args": [], "result": "hello!\n6:f64" }, { "name": "array_map_class", "args": [], "result": "xxx!\nhello!\n6:f64" }, { "name": "array_map_interface", "args": [], "result": "!\nxxx!!\n3:f64" }, { "name": "array_map_number_array", "args": [], "result": "2:f64" }, { "name": "array_map_string_array", "args": [], "result": "world^\n3:f64" }, { "name": "array_map_func", "args": [], "result": "454:f64" } ] }, { "module": "array_fill", "entries": [ { "name": "array_fill_number", "args": [], "result": "0 : 123\n1 : 234\n2 : 0\n3 : 0\n4 : 0\n5 : 0\n6 : 0\n0 : 123\n1 : 234\n2 : 0\n3 : 0\n4 : 0\n5 : 0\n6 : 0" }, { "name": "array_fill_string", "args": [], "result": "0 : hello\n1 : hello\n2 : hello\n3 : hello\n4 : hello\n5 : hello\n6 : hello\n0 : hello\n1 : hello\n2 : hello\n3 : hello\n4 : hello\n5 : hello\n6 : hello" } ] }, { "module": "array_foreach", "entries": [ { "name": "array_foreach_number", "args": [], "result": "0 : 123\n1 : 234\n2 : 456\n3 : 4\n4 : 453\n5 : 0\n6 : 456" }, { "name": "array_foreach_string", "args": [], "result": "0 : s123\n1 : s234\n2 : s456\n3 : s4\n4 : s453\n5 : s0\n6 : s456" }, { "name": "array_foreach_closure", "args": [], "result": "1:f64" } ] }, { "module": "array_slice", "entries": [ { "name": "array_slice", "args": [], "result": "0 : 0\n1 : 456" }, { "name": "array_slice_empty", "args": [], "result": "" }, { "name": "array_slice_endIdxisUndefined", "args": [], "result": "0 : a\n1 : b\n2 : c" }, { "name": "array_set_length", "args": [], "result": "d\ne\nf\ng" } ] }, { "module": "array_sort", "entries": [ { "name": "array_sort_number", "args": [], "result": "0 : 0\n1 : 4\n2 : 6\n3 : 12\n4 : 23\n5 : 45\n6 : 56\n0 : 0\n1 : 4\n2 : 6\n3 : 12\n4 : 23\n5 : 45\n6 : 56" } ] }, { "module": "array_findIndex", "entries": [ { "name": "array_findIndex_number", "args": [], "result": "foundIndex: 2\nnotfoundIndex: -1" }, { "name": "array_findIndex_string", "args": [], "result": "result: 3\nnoresult: -1" }, { "name": "array_findIndex_boolean", "args": [], "result": "1" }, { "name": "array_findIndex_class", "args": [], "result": "1\n-1" }, { "name": "array_findIndex_interface", "args": [], "result": "1" } ] }, { "module": "array_join", "entries": [ { "name": "array_join_string", "args": [], "result": "hello$wasm$hello$world\n22\nhello,wasm,hello,world\n22" } ] }, { "module": "array_concat", "entries": [ { "name": "array_concat_number", "args": [], "result": "4:f64" }, { "name": "array_concat_boolean", "args": [], "result": "4:f64" }, { "name": "array_concat_string", "args": [], "result": "4:f64" }, { "name": "array_concat_class", "args": [], "result": "5:f64" }, { "name": "array_concat_interface", "args": [], "result": "3:f64" }, { "name": "array_concat_number_array", "args": [], "result": "4:f64" }, { "name": "array_concat_string_array", "args": [], "result": "hello\n2:f64" } ] }, { "module": "array_find", "entries": [ { "name": "array_find_number", "args": [], "result": "300\nundefined" }, { "name": "array_find_boolean", "args": [], "result": "true" }, { "name": "array_find_string", "args": [], "result": "hello" }, { "name": "array_find_obj", "args": [], "result": "true" } ] }, { "module": "array_indexOf", "entries": [ { "name": "array_indexOf_number", "args": [], "result": "3:f64" }, { "name": "array_indexOf_boolean", "args": [], "result": "1:f64" }, { "name": "array_indexOf_string", "args": [], "result": "3:f64" }, { "name": "array_indexOf_class", "args": [], "result": "1:f64" } ] }, { "module": "array_lastIndexOf", "entries": [ { "name": "array_lastIndexOf_number", "args": [], "result": "3:f64" }, { "name": "array_lastIndexOf_boolean", "args": [], "result": "1:f64" }, { "name": "array_lastIndexOf_string", "args": [], "result": "3:f64" }, { "name": "array_lastIndexOf_class", "args": [], "result": "2:f64" } ] }, { "module": "array_reverse", "entries": [ { "name": "array_reverse_number", "args": [], "result": "3:f64" }, { "name": "array_reverse_boolean", "args": [], "result": "3:f64" }, { "name": "array_reverse_string", "args": [], "result": "2:f64" }, { "name": "array_reverse_class", "args": [], "result": "2:f64" }, { "name": "array_reverse_interface", "args": [], "result": "2:f64" }, { "name": "array_reverse_number_array", "args": [], "result": "44\n8:f64" }, { "name": "array_reverse_string_array", "args": [], "result": "123\n3:f64" } ] }, { "module": "array_shift", "entries": [ { "name": "array_shift_number", "args": [], "result": "2:f64" }, { "name": "array_shift_boolean", "args": [], "result": "2:f64" }, { "name": "array_shift_string", "args": [], "result": "1:f64" }, { "name": "array_shift_class", "args": [], "result": "1:f64" }, { "name": "array_shift_interface", "args": [], "result": "1:f64" }, { "name": "array_shift_number_array", "args": [], "result": "1\n6:f64" }, { "name": "array_shift_string_array", "args": [], "result": "h\n2:f64" } ] }, { "module": "array_splice", "entries": [ { "name": "array_splice_number", "args": [], "result": "0\n2:f64" }, { "name": "array_splice_boolean", "args": [], "result": "1:f64" }, { "name": "array_splice_string", "args": [], "result": "1:f64" }, { "name": "array_splice_class", "args": [], "result": "1:f64" }, { "name": "array_splice_interface", "args": [], "result": "1:f64" } ] }, { "module": "array_every", "entries": [ { "name": "array_every_number", "args": [], "result": "false\ntrue" }, { "name": "array_every_boolean", "args": [], "result": "false\ntrue" }, { "name": "array_every_string", "args": [], "result": "false\ntrue" }, { "name": "array_every_class", "args": [], "result": "false\ntrue" }, { "name": "array_every_interface", "args": [], "result": "false\ntrue" } ] }, { "module": "array_some", "entries": [ { "name": "array_some_number", "args": [], "result": "false\ntrue" }, { "name": "array_some_boolean", "args": [], "result": "false\ntrue" }, { "name": "array_some_string", "args": [], "result": "false\ntrue" }, { "name": "array_some_class", "args": [], "result": "false\ntrue" }, { "name": "array_some_interface", "args": [], "result": "false\ntrue" } ] }, { "module": "array_includes", "entries": [ { "name": "array_includes_number", "args": [], "result": "false\nfalse\nfalse\nfalse\nfalse\ntrue\ntrue\ntrue\ntrue" }, { "name": "array_includes_boolean", "args": [], "result": "false\ntrue\ntrue" }, { "name": "array_includes_string", "args": [], "result": "false\nfalse\ntrue\ntrue\nfalse" }, { "name": "array_includes_class", "args": [], "result": "false\nfalse\ntrue\ntrue" }, { "name": "array_includes_interface", "args": [], "result": "false\nfalse\ntrue\ntrue" } ] }, { "module": "array_reduce", "entries": [ { "name": "array_reduce_number", "args": [], "result": "21" }, { "name": "array_reduce_boolean", "args": [], "result": "false" }, { "name": "array_reduce_string", "args": [], "result": "01234" }, { "name": "array_reduce_class", "args": [], "result": "312345" }, { "name": "array_reduce_interface", "args": [], "result": "A2" } ] }, { "module": "array_reduceRight", "entries": [ { "name": "array_reduceRight_number", "args": [], "result": "21" }, { "name": "array_reduceRight_boolean", "args": [], "result": "true" }, { "name": "array_reduceRight_string", "args": [], "result": "04321" }, { "name": "array_reduceRight_class", "args": [], "result": "354321" }, { "name": "array_reduceRight_interface", "args": [], "result": "A1" } ] }, { "module": "array_copyWithin", "entries": [ { "name": "array_copyWithin_number", "args": [], "result": "6:f64" }, { "name": "array_copyWithin_boolean", "args": [], "result": "false\nfalse\ntrue" }, { "name": "array_copyWithin_string", "args": [], "result": "2\n3\n3\n4\n5" }, { "name": "array_copyWithin_class", "args": [], "result": "A2\nA2\nA3" }, { "name": "array_copyWithin_interface", "args": [], "result": "A2\nA2\nA3" } ] }, { "module": "block_closure", "entries": [ { "name": "getRes", "args": [], "result": "1\n5\n2\n10\n10\n10\n1\n5\n11\n15\n2\n10\n10\n10\n1\n5" } ] }, { "module": "block_inner_block", "entries": [ { "name": "innerBlock", "args": [], "result": "11:f64" } ] }, { "module": "boolean_basic", "entries": [ { "name": "booleanBasicTrue", "args": [], "result": "0x1:i32" }, { "name": "booleanBasicFalse", "args": [], "result": "0x0:i32" }, { "name": "booleanCmp", "args": [], "result": "false\ntrue" } ] }, { "module": "boolean_logic_operator", "entries": [ { "name": "logicOr", "args": [], "result": "0x1:i32" }, { "name": "logicAnd", "args": [], "result": "0x0:i32" }, { "name": "conditionExpr", "args": [], "result": "2:f64" } ] }, { "module": "boolean_not", "entries": [ { "name": "notNumber", "args": [], "result": "0x0:i32" }, { "name": "notBoolean", "args": [], "result": "0x1:i32" }, { "name": "notWithLogicOperator", "args": [], "result": "0:f64" } ] }, { "module": "builtin_array", "entries": [ { "name": "length", "args": [], "result": "3:f64" }, { "name": "isArray_anyType", "args": [], "result": "0x1:i32" }, { "name": "isArray_arrayType", "args": [], "result": "0x1:i32" } ] }, { "module": "builtin_console", "entries": [ { "name": "consoleLog", "args": [], "result": "1 true 123 [object Object]" }, { "name": "specialNum", "args": [], "result": "NaN\nInfinity\n-Infinity" } ] }, { "module": "builtin_math", "entries": [ { "name": "mathSqrt", "args": [], "result": "3:f64" }, { "name": "mathMaxWithOneOperation", "args": [], "result": "3:f64" }, { "name": "mathMaxWithMultiOperation", "args": [], "result": "9:f64" }, { "name": "mathMinWithOneOperation", "args": [], "result": "2:f64" }, { "name": "mathMinWithMultiOperation", "args": [], "result": "1:f64" }, { "name": "mathPowWithZero", "args": [], "result": "1:f64" }, { "name": "mathPowWithNegative", "args": [], "result": "0.1111111:f64" }, { "name": "mathPowWithPositive", "args": [], "result": "9:f64" }, { "name": "mathAbs", "args": [], "result": "3:f64" }, { "name": "mathNested", "args": [], "result": "27:f64" } ] }, { "module": "builtin_string", "entries": [ { "name": "stringConcat", "args": [], "result": "helloworld" }, { "name": "stringLength", "args": [], "result": "5:f64" }, { "name": "stringSliceWithTwoNegativeNumber", "args": [], "result": "" }, { "name": "stringSliceWithTwoPositiveNumber", "args": [], "result": "el" }, { "name": "stringSliceWithTwoUndefind", "args": [], "result": "hello" }, { "name": "stringSliceWithOneZero", "args": [], "result": "ab" }, { "name": "stringIndexOf", "args": [], "result": "9\n-1\n-1:f64" }, { "name": "stringSplit", "args": [], "result": "h\ne\nl\nl\no\n\nllo\n\nref.struct" }, { "name": "stringReplace", "args": [], "result": "hellokhello\nhellokhello\nhello-hello\n-hellokhello\nhellohello" }, { "name": "stringMatch", "args": [], "result": "hello\norld\n\nref.struct" }, { "name": "stringSearch", "args": [], "result": "0\n7\n-1\n0\n0:f64" }, { "name": "stringcharAt", "args": [], "result": "h" }, { "name": "stringtoLowerCase", "args": [], "result": "hello" }, { "name": "stringtoUpperCase", "args": [], "result": "HELLO" }, { "name": "stringtrim", "args": [], "result": "hello" }, { "name": "stringreadonly", "args": [], "result": "e" }, { "name": "stringadd", "args": [], "result": "hello world" }, { "name": "stringFromCharCode", "args": [], "result": "h\nijk" } ] }, { "module": "call_expression_function_hoisting", "entries": [ { "name": "funcHosting", "args": [], "result": "110:f64" } ] }, { "module": "call_expression_get_value", "entries": [ { "name": "getValueWithDefaultParam", "args": [], "result": "116:f64" }, { "name": "callInnerFunc", "args": [ 18, 29 ], "result": "56:f64" }, { "name": "recursive", "args": [ 5 ], "result": "5:f64" } ] }, { "module": "call_expression_param", "entries": [ { "name": "noDefaultParam", "args": [], "result": "6:f64" }, { "name": "allDefaultParam", "args": [], "result": "6:f64" }, { "name": "someDefaultParam", "args": [], "result": "5:f64" }, { "name": "someDefaultParamWithAny", "args": [], "result": "17:f64" }, { "name": "paramIsAny", "args": [], "result": "3:f64" } ] }, { "module": "cast_any_to_static", "entries": [ { "name": "castAnyBackToClass", "args": [], "result": "0x1:i32" }, { "name": "castAnyBackToString", "args": [], "result": "hi" }, { "name": "castAnyBackToNumber", "args": [], "result": "1:f64" }, { "name": "castAnyBackToNull", "args": [], "result": "any:ref.null" }, { "name": "castAnyBackToBoolean", "args": [], "result": "0x1:i32" }, { "name": "castAnyBackToUndefined", "args": [], "result": "ref.struct" } ] }, { "module": "class_basic", "entries": [ { "name": "withoutCtor", "args": [], "result": "123:f64" }, { "name": "basic", "args": [], "result": "10:f64" }, { "name": "getterSetter", "args": [], "result": "25:f64" }, { "name": "anyType", "args": [], "result": "10:f64" }, { "name": "defaultCtor", "args": [], "result": "3:f64" }, { "name": "classNestCall", "args": [], "result": "20:f64" }, { "name": "thisAsFreeVar", "args": [], "result": "22" }, { "name": "classInClosure", "args": [], "result": "1\n2" }, { "name": "test", "args": [], "result": "click" }, { "name": "methodCallAndGet", "args": [], "result": "test\n[wasm object]" } ] }, { "module": "class_accessor", "entries": [ { "name": "test1", "args": [], "result": "2" }, { "name": "test2", "args": [], "result": "undefined" }, { "name": "test3", "args": [], "result": "1" }, { "name": "test4", "args": [], "result": "2" }, { "name": "test5", "args": [], "result": "undefined" }, { "name": "test6", "args": [], "result": "2" }, { "name": "test7", "args": [], "result": "2" }, { "name": "test8", "args": [], "result": "2" }, { "name": "test9", "args": [], "result": "undefined" }, { "name": "test10", "args": [], "result": "undefined" }, { "name": "test11", "args": [], "result": "undefined" }, { "name": "test12", "args": [], "result": "1" }, { "name": "test13", "args": [], "result": "1" }, { "name": "test14", "args": [], "result": "1" }, { "name": "test15", "args": [], "result": "undefined\n2\n1\n1\n2" }, { "name": "testOnlySetter", "args": [], "result": "invoke foo\ninvoke bar\n10\n100" }, { "name": "testOnlyGetter", "args": [], "result": "invoke foo\ninvoke bar\n10\n100" } ] }, { "module": "class_direct_call", "entries": [ { "name": "foo", "args": [], "result": "a\nb" }, { "name": "test", "args": [], "result": "1\n11\n3\n0\n2" } ] }, { "module": "class_extend", "entries": [ { "name": "extendWithNewProp", "args": [], "result": "90:f64" }, { "name": "methodOverwrite", "args": [], "result": "40:f64" }, { "name": "multiLevelExtend", "args": [], "result": "B" }, { "name": "testExtendReordered", "args": [], "result": "22:f64" }, { "name": "testInheritGetter", "args": [], "result": "10:f64" }, { "name": "inheritCast", "args": [], "result": "foo\nbase\nfoo\nbar" }, { "name": "extendsImpl", "args": [], "result": "true\n1\ntrue" }, { "name": "implInfc", "args": [], "result": "1\nfalse" }, { "name": "fieldInitOrder", "args": [], "result": "100:f64" }, { "name": "superWithOptionlParam", "args": [], "result": "hello" } ] }, { "module": "class_field_assign", "entries": [ { "name": "withCtor", "args": [], "result": "18:f64" }, { "name": "withoutCtor", "args": [], "result": "0x0:i32" }, { "name": "withEnum", "args": [], "result": "1:f64" } ] }, { "module": "class_static_prop", "entries": [ { "name": "staticMethodWithOverwrite", "args": [], "result": "2:f64" }, { "name": "staticMethod", "args": [], "result": "1:f64" }, { "name": "staticFields", "args": [], "result": "74:f64" }, { "name": "testStaticField1", "args": [], "result": "0:f64" }, { "name": "testStaticField2", "args": [], "result": "1:f64" }, { "name": "testStaticField3", "args": [], "result": "2:f64" }, { "name": "staticFieldWithOverwrite", "args": [], "result": "3.14\n3.14\n100\n314" } ] }, { "module": "class_type", "entries": [ { "name": "uniqueType", "args": [], "result": "1:f64" } ] }, { "module": "class_vtable_accessor", "entries": [ { "name": "vtableAccessor", "args": [], "result": "90\n888" } ] }, { "module": "class_vtable_call", "entries": [ { "name": "bar", "args": [], "result": "a\nb" } ] }, { "module": "closure_basic", "entries": [ { "name": "accessOuterVars", "args": [], "result": "4:f64" }, { "name": "returnOuterFuncCall", "args": [], "result": "10:f64" }, { "name": "returnOuterFuncCall2", "args": [], "result": "4:f64" }, { "name": "accesssGlobalVar", "args": [], "result": "ref.struct" }, { "name": "classFieldIsClosure", "args": [], "result": "20" }, { "name": "classFieldIsClosureWithDefault", "args": [], "result": "41" } ] }, { "module": "closure_first_class_func", "entries": [ { "name": "returnAFunction", "args": [], "result": "3:f64" }, { "name": "functionAsParam", "args": [], "result": "10:f64" } ] }, { "module": "closure_inner_func", "entries": [ { "name": "outerFunc", "args": [], "result": "2\n1" }, { "name": "outerFuncWithBlock", "args": [], "result": "52\n100" } ] }, { "module": "closure_set_ctx_value", "entries": [ { "name": "setCtxValue", "args": [ 9 ], "result": "30:f64" } ] }, { "module": "comments_with_export", "entries": [ { "name": "nameB", "args": [], "result": "exportName is nameB" }, { "name": "nameBNotInEntry", "args": [], "result": "exportName is nameBNotInEntry" }, { "name": "nameD", "args": [1, 1], "result": "exportName is nameD" } ] }, { "module": "complexType_case1", "entries": [ { "name": "complexTypeTest", "args": [], "result": "5:f64" } ] }, { "module": "complexType_case2", "entries": [ { "name": "cpxCase2Func3", "args": [ 0 ], "result": "ref.struct" } ] }, { "module": "complexType_case3", "entries": [ { "name": "cpxCase3Func1", "args": [], "result": "10:f64" } ] }, { "module": "complexType_case4", "entries": [ { "name": "cpxCase3Func1", "args": [], "result": "3:f64" } ] }, { "module": "complexType_case5", "entries": [ { "name": "cpxCase3Func1", "args": [], "result": "6:f64" } ] }, { "module": "dataview_basic", "entries": [ { "name": "getDataViewProperty", "args": [], "result": "5\n1" }, { "name": "newDataView", "args": [], "result": "5\n-5\n10\n0\n5\n5" }, { "name": "dataViewI8", "args": [], "result": "0\n5\n-5" }, { "name": "dataViewI16", "args": [], "result": "5\n0\n5\n1280\n0\n5\n1280\n5\n-5\n-1\n-5\n-1025\n-1\n-5\n-1025\n-5" }, { "name": "dataViewI32", "args": [], "result": "5\n1280\n5\n83886080\n0\n0\n83886080\n5\n-5\n-1025\n-5\n-67108865\n-1\n-1\n-67108865\n-5" }, { "name": "dataViewUi8", "args": [], "result": "5\n5\n-5\n251\n5\n5\n-5\n251" }, { "name": "dataViewUi16", "args": [], "result": "5\n5\n-5\n65531\n5\n5\n-5\n65531" }, { "name": "dataViewUi32", "args": [], "result": "5\n5\n-5\n4294967291\n5\n5\n-5\n4294967291" }, { "name": "dataViewF32", "args": [], "result": "1074790400\n2.25\n-1086324736\n-0.75" }, { "name": "dataViewF64", "args": [], "result": "0\n0\n2.25\n-1075314688\n-1.8125\n-0.75" } ] }, { "module": "declare_class", "entries": [ { "name": "classDecl", "args": [], "result": "need_manual_fill" } ] }, { "module": "declare_func", "entries": [ { "name": "assignDeclareFuncToVar", "args": [], "result": "need_manual_fill" } ] }, { "module": "declare_namespace", "entries": [] }, { "module": "declare_var", "entries": [] }, { "module": "default_param", "entries": [ { "name": "defaultParamInMethod", "args": [], "result": "8" }, { "name": "defaultParamInStaticMethod", "args": [], "result": "8" }, { "name": "defaultParamInFunction", "args": [], "result": "8" } ] }, { "module": "export_class", "entries": [] }, { "module": "export_func", "entries": [ { "name": "defaultFunc", "args": [], "result": "100:f64" }, { "name": "addFunc", "args": [ 21, 22 ], "result": "43:f64" }, { "name": "subFunc", "args": [ 21, 22 ], "result": "-1:f64" }, { "name": "mulFunc", "args": [ 2, 3 ], "result": "6:f64" }, { "name": "divFunc", "args": [ 20, 10 ], "result": "2:f64" } ] }, { "module": "export_func_invoked", "entries": [ { "name": "beCalledFunc", "args": [], "result": "2:f64" }, { "name": "exportedFunc", "args": [], "result": "2:f64" } ] }, { "module": "export_namespace", "entries": [ { "name": "bFunc", "args": [], "result": "10:f64" } ] }, { "module": "export_var", "entries": [] }, { "module": "expression_binary", "entries": [ { "name": "lt", "args": [], "result": "1:f64" }, { "name": "gt", "args": [], "result": "1:f64" }, { "name": "le", "args": [], "result": "1:f64" }, { "name": "ge", "args": [], "result": "1:f64" }, { "name": "eq", "args": [], "result": "1:f64" }, { "name": "seenAsEq", "args": [], "result": "1:f64" }, { "name": "ne", "args": [], "result": "1:f64" }, { "name": "seenAsNe", "args": [], "result": "1:f64" }, { "name": "add", "args": [], "result": "3:f64" }, { "name": "sub", "args": [], "result": "1:f64" }, { "name": "mul", "args": [], "result": "4:f64" }, { "name": "div", "args": [], "result": "1:f64" }, { "name": "subEq", "args": [], "result": "1:f64" }, { "name": "addEq", "args": [], "result": "3:f64" }, { "name": "mulEq", "args": [], "result": "6:f64" }, { "name": "divEq", "args": [], "result": "2:f64" }, { "name": "xor", "args": [], "result": "4656\n-2147483648\n4656\n-2147483648" }, { "name": "shl", "args": [], "result": "36\n2\n-4\n-2" }, { "name": "shr", "args": [], "result": "2\n-1073741824\n-1\n1073741823\n1073741824\n1073741823" }, { "name": "and", "args": [], "result": "516\n1\n-13176\n2147483647" }, { "name": "or", "args": [], "result": "13173\n-2147483647\n-13176\n2147483647" } ] }, { "module": "expression_binary_select", "entries": [ { "name": "selectAmpersandTrueTrue", "args": [], "result": "20:f64" }, { "name": "selectAmpersandTrueFalse", "args": [], "result": "0:f64" }, { "name": "selectAmpersandFalseTrue", "args": [], "result": "0:f64" }, { "name": "selectAmpersandFalseFlase", "args": [], "result": "0:f64" }, { "name": "selectBarTrueTrue", "args": [], "result": "10:f64" }, { "name": "selectBarTrueFalse", "args": [], "result": "1:f64" }, { "name": "selectBarFalseTrue", "args": [], "result": "1:f64" }, { "name": "selectBarFalseFalse", "args": [], "result": "0:f64" } ] }, { "module": "expression_condition", "entries": [ { "name": "binaryAsCondition", "args": [], "result": "1:f64" } ] }, { "module": "expression_unary", "entries": [ { "name": "prefixUnaryPlusPlus", "args": [], "result": "3:f64" }, { "name": "prefixUnaryMinusMinus", "args": [], "result": "-1:f64" }, { "name": "prefixUnaryExclamation", "args": [], "result": "1:f64" }, { "name": "prefixUnaryMinusToLiteralWithBinaryExpr", "args": [], "result": "-1:f64" }, { "name": "prefixUnaryMinusToLiteralWithVarStmt", "args": [], "result": "-1:f64" }, { "name": "prefixUnaryMinusToVarWithBinaryExpr", "args": [], "result": "-1:f64" }, { "name": "prefixUnaryMinusToVarWithVarStmt", "args": [], "result": "-1:f64" }, { "name": "prefixUnaryPlus", "args": [], "result": "1:f64" } ] }, { "module": "function_declaration", "entries": [ { "name": "simpleFunctionOnlyReturn", "args": [], "result": "1:f64" }, { "name": "basicFunction", "args": [ 37, 29 ], "result": "66:f64" }, { "name": "defaultParamExport", "args": [], "result": "3:f64" }, { "name": "functionWithFuncScopeVariable", "args": [], "result": "5:f64" }, { "name": "miltipleVariablesInOneStatement", "args": [], "result": "7:f64" } ] }, { "module": "function_expression", "entries": [ { "name": "functionExpression", "args": [], "result": "2004.1:f64" }, { "name": "arrowFunction", "args": [], "result": "2:f64" }, { "name": "arrowFunctionWithoutReturn", "args": [], "result": "1\n2:f64" }, { "name": "functionReturnClosure", "args": [], "result": "2\nfoo\n5" } ] }, { "module": "function_scope_var", "entries": [ { "name": "useBeforeDeclare", "args": [], "result": "1:f64" }, { "name": "operateWithConst", "args": [], "result": "16:f64" }, { "name": "nestedFunction", "args": [], "result": "10:f64" } ] }, { "module": "if_statement_case1", "entries": [ { "name": "ifElse", "args": [], "result": "26:f64" }, { "name": "nestedIf", "args": [], "result": "36:f64" }, { "name": "noElseBranch", "args": [], "result": "17:f64" }, { "name": "returnInIf", "args": [ 20 ], "result": "20:f64" }, { "name": "stringAsCond", "args": [], "result": "b\nb" }, { "name": "paramUndefined", "args": [], "result": "else when not undefined\nparams is undefined" }, { "name": "paramNotUndefined", "args": [], "result": "params is not undefined" } ] }, { "module": "import_alias_identifier", "entries": [ { "name": "getAliasFunc", "args": [], "result": "10:f64" }, { "name": "getAliasVar", "args": [], "result": "100:f64" }, { "name": "getAliasClass", "args": [], "result": "50:f64" }, { "name": "getAliasNS", "args": [], "result": "88:f64" } ] }, { "module": "import_alias_reexport_identifier", "entries": [ { "name": "getAliasFunc", "args": [], "result": "10:f64" }, { "name": "getAliasVar", "args": [], "result": "100:f64" }, { "name": "getAliasClass", "args": [], "result": "50:f64" }, { "name": "getAliasNS", "args": [], "result": "88:f64" } ] }, { "module": "import_alias_scope_identifier", "entries": [ { "name": "getAliasFunc", "args": [], "result": "10:f64" }, { "name": "getAliasVar", "args": [], "result": "100:f64" }, { "name": "getAliasClass", "args": [], "result": "50:f64" }, { "name": "getAliasNS", "args": [], "result": "88:f64" } ] }, { "module": "import_class", "entries": [ { "name": "importClass", "args": [], "result": "10:f64" }, { "name": "importClassAsBaseClass", "args": [], "result": "8:f64" }, { "name": "importClassAsInterface", "args": [], "result": "2:f64" } ] }, { "module": "import_expression", "entries": [ { "name": "defaultObjLiteral", "args": [], "result": "2:f64" }, { "name": "defaultNumberLiteral", "args": [], "result": "100:f64" } ] }, { "module": "import_func", "entries": [ { "name": "importFuncAdd", "args": [], "result": "3:f64" }, { "name": "importFuncSub", "args": [], "result": "-1:f64" }, { "name": "importFuncMul", "args": [], "result": "2:f64" }, { "name": "importFuncDiv", "args": [], "result": "2:f64" }, { "name": "importDefaultFunc", "args": [], "result": "100:f64" }, { "name": "importFuncInvoked", "args": [], "result": "2:f64" }, { "name": "importFuncByExportFrom", "args": [], "result": "hello" } ] }, { "module": "import_implicit_type", "entries": [ { "name": "getObjTypeFromImport", "args": [], "result": "1\nhi" } ] }, { "module": "import_namespace", "entries": [ { "name": "importNamespaceFunc", "args": [], "result": "10:f64" }, { "name": "importNamespaceVar", "args": [], "result": "8:f64" } ] }, { "module": "import_type", "entries": [ { "name": "validatePrimitiveType", "args": [], "result": "100\nhi\ntrue" }, { "name": "validateObjType", "args": [], "result": "hello\n18" }, { "name": "validateFuncType", "args": [], "result": "19:f64" }, { "name": "validateTypeArguments", "args": [], "result": "10\nfalse" } ] }, { "module": "import_var", "entries": [ { "name": "importVarA", "args": [], "result": "10:f64" }, { "name": "importVarB", "args": [], "result": "110:f64" }, { "name": "importVarC", "args": [], "result": "1000:f64" } ] }, { "module": "infc_assign_class", "entries": [ { "name": "classAndInfc", "args": [], "result": "1:f64" }, { "name": "infcImpl", "args": [], "result": "21:f64" }, { "name": "removeInfcFromRecGroup", "args": [], "result": "1:f64" }, { "name": "infcImplWithOptionalField", "args": [], "result": "2" }, { "name": "infcCastToObject", "args": [], "result": "-1 -2" } ] }, { "module": "infc_assign_infc", "entries": [ { "name": "infcAndInfc", "args": [], "result": "10:f64" } ] }, { "module": "infc_assign_obj", "entries": [ { "name": "objLiteralAndInfc", "args": [], "result": "1:f64" } ] }, { "module": "infc_assign_obj", "entries": [ { "name": "infcInitGlobal", "args": [], "result": "12:f64" } ] }, { "module": "infc_field_assign", "entries": [ { "name": "fieldAssignToOther", "args": [], "result": "1:f64" }, { "name": "otherAssignToField", "args": [], "result": "20:f64" } ] }, { "module": "infc_method", "entries": [ { "name": "infcSetter", "args": [], "result": "10:f64" }, { "name": "infcMethod", "args": [], "result": "0x0:i32" }, { "name": "infcGetter", "args": [], "result": "1:f64" }, { "name": "infcNestMethod", "args": [], "result": "6:f64" }, { "name": "infcMethodWithAnyInst", "args": [], "result": "1:f64" }, { "name": "ObjectLiteralFunctionFieldToInfc", "args": [], "result": "Hello" }, { "name": "ClassFunctionFieldToInfc", "args": [], "result": "Hello" } ] }, { "module": "infc_parameter", "entries": [ { "name": "infcToClass", "args": [], "result": "Exception: unreachable", "ret": 1 }, { "name": "classToInfc", "args": [], "result": "2:f64" }, { "name": "infcAsParameter", "args": [], "result": "0x0:i32" } ] }, { "module": "infc_return_value", "entries": [ { "name": "returnInfc", "args": [], "result": "0x1:i32" }, { "name": "returnClass", "args": [], "result": "Exception: unreachable", "ret": 1 } ] }, { "module": "infc_with_array", "entries": [ { "name": "infcWithArray", "args": [], "result": "12:f64" } ] }, { "module": "instanceof", "entries": [ { "name": "instanceofTest", "args": [], "result": "true\ntrue\nfalse\ntrue\nfalse\ntrue\nfalse\nfalse\na\nb\ntrue\ntrue\nfalse" }, { "name": "leftBaseRightSuper", "args": [], "result": "is Base_1" } ] }, { "module": "lib", "entries": [ { "name": "test", "args": [], "result": "hello" } ] }, { "module": "loop_do_while", "entries": [ { "name": "loopBodyEmpty", "args": [], "result": "10:f64" }, { "name": "basicDoLoop", "args": [], "result": "100:f64" }, { "name": "prefixPlusPlus", "args": [], "result": "21:f64" }, { "name": "suffixPlusPlus", "args": [], "result": "22:f64" }, { "name": "numberAsCondition", "args": [], "result": "0:f64" }, { "name": "doWhileWithContinue", "args": [], "result": "0x1:i32" } ] }, { "module": "loop_for", "entries": [ { "name": "basicCase", "args": [], "result": "106:f64" }, { "name": "loopBodySemicolon", "args": [], "result": "100:f64" }, { "name": "loopBodyEmpty", "args": [], "result": "100:f64" }, { "name": "noInitializer", "args": [], "result": "90:f64" }, { "name": "noCondition", "args": [], "result": "88:f64" }, { "name": "noIncrement", "args": [], "result": "90:f64" }, { "name": "nestedForLoopWithBreak", "args": [], "result": "105:f64" }, { "name": "multipleForLoop", "args": [], "result": "115:f64" }, { "name": "loopWithCommaToken", "args": [], "result": "176:f64" }, { "name": "loopWithContinue", "args": [], "result": "0x1:i32" } ] }, { "module": "loop_while", "entries": [ { "name": "basicLoop", "args": [], "result": "10:f64" }, { "name": "loopBodyEmpty", "args": [], "result": "100:f64" }, { "name": "loopBodySemicolon", "args": [], "result": "100:f64" }, { "name": "complexLoop", "args": [], "result": "49:f64" }, { "name": "whileWithContinue", "args": [], "result": "0x1:i32" } ] }, { "module": "module_start_A", "entries": [ { "name": "getAVar", "args": [], "result": "1140:f64" }, { "name": "getBVar", "args": [], "result": "120:f64" }, { "name": "getCVar", "args": [], "result": "20:f64" } ] }, { "module": "module_start_B", "entries": [ { "name": "getBVar", "args": [], "result": "120:f64" }, { "name": "getCVar", "args": [], "result": "20:f64" } ] }, { "module": "module_start_C", "entries": [ { "name": "getCVar", "args": [], "result": "20:f64" } ] }, { "module": "namespace_func", "entries": [ { "name": "namespaceFunc", "args": [], "result": "2:f64" } ] }, { "module": "namespace_nest", "entries": [ { "name": "namespaceNested", "args": [], "result": "-1:f64" } ] }, { "module": "namespace_var", "entries": [ { "name": "namespaceVar", "args": [], "result": "1:f64" } ] }, { "module": "nest_generic", "entries": [ { "name": "test1", "args": [], "result": "1\ntrue\n2\nhello" }, { "name": "test2", "args": [], "result": "1\nhello" }, { "name": "test3", "args": [], "result": "3\n2\n3\n1\n33\n22\n33\nhello" }, { "name": "test4", "args": [], "result": "1\nhello\n2\n1\n3\nworld\nhello\n111" }, { "name": "test5", "args": [], "result": "1\nhello\nfalse\n2" }, { "name": "test6", "args": [], "result": "2\n2\ns1 11\ns2 22\ns3 33\n33\n0\n11\n22\ntrue\n0" } ] }, { "module": "null_type_case1", "entries": [ { "name": "nullTypeTest", "args": [], "result": "20:f64" } ] }, { "module": "obj_property_access", "entries": [ { "name": "infc_obj_get_field", "args": [], "result": "1" }, { "name": "infc_obj_set_field", "args": [], "result": "100" }, { "name": "obj_get_field", "args": [], "result": "1" }, { "name": "obj_set_field", "args": [], "result": "100" }, { "name": "obj_get_method", "args": [], "result": "1" }, { "name": "infc_obj_get_instance_method", "args": [], "result": "hi" }, { "name": "infc_obj_get_vtable_method", "args": [], "result": "5" }, { "name": "infc_obj_set_method", "args": [], "result": "100" }, { "name": "obj_set_method", "args": [], "result": "100" }, { "name": "getNullableFunc", "args": [], "result": "cbFunc is null\ncbFunc is not null\nrun cb" } ] }, { "module": "obj_property_dynamic_access", "entries": [ { "name": "dynamicGetUnboxingInObjLiteral", "args": [], "result": "1\n1\n100" }, { "name": "dynamicGetBoxingInObjLiteral", "args": [], "result": "8\n8\n100" }, { "name": "dynamicGetUnboxingInClass", "args": [], "result": "10\n1\n100" }, { "name": "dynamicGetBoxingInClass", "args": [], "result": "10\n1\n100" }, { "name": "dynamicSetUnboxingInClass", "args": [], "result": "5\n100" }, { "name": "dynamicSetBoxingInClass", "args": [], "result": "10\n100" }, { "name": "dynamicAccessInUnionType", "args": [], "result": "11" } ] }, { "module": "obj_literal", "entries": [ { "name": "simpleObject", "args": [], "result": "1:f64" }, { "name": "nestedObject", "args": [], "result": "4:f64" }, { "name": "moreNestedObject", "args": [], "result": "0x0:i32" }, { "name": "assignObjectLiteralToField", "args": [], "result": "6:f64" }, { "name": "withMethodField", "args": [], "result": "117:f64" }, { "name": "structWithSameLayout", "args": [], "result": "1:f64" }, { "name": "useThisInLiteralObj", "args": [], "result": "A\n1\nA\nB\n1\nB\na2\n1\na2" }, { "name": "infcInitWithLiteralObj_completion", "args": [], "result": "10\nundefined\nundefined\nundefined\nundefined\nfalse" }, { "name": "infcInitWithLiteralObj_reorder", "args": [], "result": "10\nhello\nfalse" }, { "name": "assignClassFieldWithObjectLiteral", "args": [], "result": "hello" }, { "name": "assignInfcFieldWithObjectLiteral", "args": [], "result": "world" } ] }, { "module": "obj_method_call", "entries": [ { "name": "callClassTypedFunc", "args": [], "result": "METHOD\nFIELD" }, { "name": "callClassTypedClosure", "args": [], "result": "METHOD\nFIELD" }, { "name": "callInfcTypedFunc", "args": [], "result": "METHOD\nFIELD" }, { "name": "callInfcTypedClosure", "args": [], "result": "METHOD\nFIELD" } ] }, { "module": "op_ref_type", "entries": [ { "name": "judgeIsRefNull", "args": [], "result": "both find\nat least one found\nno found" } ] }, { "module": "optional_param", "entries": [ { "name": "testOptionalParam", "args": [], "result": "30:f64" }, { "name": "testOptionalParamAndRestParam", "args": [], "result": "30:f64" }, { "name": "testOptionalMethod", "args": [], "result": "40:f64" }, { "name": "testPartOptionalMethod", "args": [], "result": "40:f64" } ] }, { "module": "parenthesized_expression_case1", "entries": [ { "name": "parenthesizedTest", "args": [], "result": "5.833333:f64" } ] }, { "module": "primitiveType", "entries": [ { "name": "constPrimitiveVars", "args": [], "result": "3:f64" }, { "name": "letPrimitiveVars", "args": [], "result": "6:f64" }, { "name": "nullundefinedCmp", "args": [], "result": "false\ntrue\ntrue\nfalse\nfalse\nfalse\ntrue\nfalse\ntrue\nfalse\ntrue\nfalse\ntrue\ntrue\nfalse" }, { "name": "NaNNumber", "args": [], "result": "1\n1" } ] }, { "module": "promise_chain", "entries": [ { "name": "promiseChain", "args": [], "result": "123\n456\nhello\nfalse\nfinally" }, { "name": "promiseMultiThen", "args": [], "result": "123\n133\n143\n153" } ] }, { "module": "promise_constructor", "entries": [ { "name": "newPromiseWithVoid", "args": [], "result": "before call resolve\nafter call resolve\n\nthen_onFulfilled_func" }, { "name": "newPromiseWithNumber", "args": [], "result": "100" }, { "name": "newPromiseWithString", "args": [], "result": "hello" } ] }, { "module": "promise_immediate", "entries": [ { "name": "immediateResolveWithNoArg", "args": [], "result": "then_onFulfilled_func" }, { "name": "immediateResolveWithArg", "args": [], "result": "then_onFulfilled_func" }, { "name": "immediateReject", "args": [], "result": "then_onRejected_func" } ] }, { "module": "promise_throw", "entries": [ { "name": "promiseThrowError", "args": [], "result": "123\n456\nfinally" }, { "name": "promiseCatchInCB", "args": [], "result": "123\n456\nfinally" }, { "name": "promiseNotCatchInCB", "args": [], "result": "123\nexception_not_catch_in_cb\nfinally" } ] }, { "module": "prototype", "entries": [ { "name": "setPrototype", "args": [], "result": "1:f64" }, { "name": "returnPrototypeObject", "args": [], "result": "ref.struct" } ] }, { "module": "ref_type_cmp", "entries": [ { "name": "structCmpEq", "args": [], "result": "0x1:i32" }, { "name": "structCmpNotEq", "args": [], "result": "0x0:i32" }, { "name": "arrayCmpEq", "args": [], "result": "0x1:i32" }, { "name": "arrayCmpNotEq", "args": [], "result": "0x0:i32" }, { "name": "infcCmpEq", "args": [], "result": "0x1:i32" }, { "name": "infcCmpNotEq", "args": [], "result": "0x0:i32" }, { "name": "infcClassCmpEq", "args": [], "result": "0x1:i32" }, { "name": "infcClassCmpNotEq", "args": [], "result": "0x0:i32" } ] }, { "module": "rest_param_interface", "entries": [ { "name": "restParameterTest", "args": [], "result": "ref.struct" } ] }, { "module": "rest_param_number", "entries": [ { "name": "restParameterTest", "args": [], "result": "47:f64" }, { "name": "undefinedAsCond", "args": [], "result": "hello\nundefined" }, { "name": "anyrefCond", "args": [], "result": "hello\nv\nv3\nv5\nv7\nv10\nv11\nv12" }, { "name": "restParamWithEmpty", "args": [], "result": "3\n0" } ] }, { "module": "return_statement", "entries": [ { "name": "deadCodeAfterReturn", "args": [ 11, 9 ], "result": "2:f64" }, { "name": "deadReturnStatement", "args": [ 17 ], "result": "17" }, { "name": "returnVoid", "args": [], "result": "before\n2" }, { "name": "returnNaN", "args": [], "result": "nan:f64" }, { "name": "returnInfiity", "args": [], "result": "inf:f64" }, { "name": "returnNegInfiity", "args": [], "result": "-inf:f64" } ] }, { "module": "scoped_variables", "entries": [ { "name": "nestedScopes", "args": [], "result": "5:f64" } ] }, { "module": "string_type", "entries": [ { "name": "stringNotReturned", "args": [], "result": "" }, { "name": "returnString", "args": [], "result": "hello" }, { "name": "assignStringToVariable", "args": [], "result": "hello" }, { "name": "noExplicitStringKeyword", "args": [], "result": "" }, { "name": "unicode", "args": [], "result": "🀄" }, { "name": "noSubstitutionTplLiteral", "args": [], "result": "hello\n\n\n This is\n a multiline\n string." }, { "name": "templateString", "args": [], "result": "1 world\n11 world\nHello world Hello world\nhello is not 10\nhi x\n1 and Hello is x\n10\nhi\nhi is x\n10 is 10\n0 is 0\n10 is 10\n1,2\n2\n1 is 1\n[object Object] is object\nhi is hi\n0 is 0\nundefined is undefined" }, { "name": "stringContainHex", "args": [], "result": "ABC" } ] }, { "module": "string_binary_operation", "entries": [ { "name": "staticStringAdd", "args": [], "result": "helloworld" }, { "name": "dynStringAdd", "args": [], "result": "helloworld" }, { "name": "staticDynStringAdd", "args": [], "result": "helloworld" }, { "name": "staticToStringAdd", "args": [], "result": "hello123\nhellotrue" }, { "name": "dynToStringAdd", "args": [], "result": "hello123\nhellotrue" } ] }, { "module": "string_or", "entries": [ { "name": "string_or", "args": [], "result": "hello\nwasm" } ] }, { "module": "switch_case_statement", "entries": [ { "name": "switchWithDefault", "args": [], "result": "0:f64" }, { "name": "nestedSwitchCase", "args": [], "result": "1:f64" }, { "name": "emptySwitch", "args": [], "result": "0:f64" }, { "name": "switchWithoutDefault", "args": [], "result": "10:f64" }, { "name": "multipleCasesShareSameBlock", "args": [], "result": "11:f64" }, { "name": "caseWithoutBlock", "args": [], "result": "10:f64" }, { "name": "caseWithoutBreak", "args": [], "result": "11:f64" }, { "name": "varDeclarationInCase", "args": [], "result": "20:f64" }, { "name": "stringInCase", "args": [], "result": "-bbb- bbb" }, { "name": "noCase", "args": [], "result": "10" }, { "name": "caseAndDefault", "args": [], "result": "130" } ] }, { "module": "this_in_closure", "entries": [ { "name": "useThisInClosure", "args": [], "result": "start\nstop\npause\nresume" } ] }, { "module": "this_in_method", "entries": [ { "name": "thisInObjLiteralArrowFunc", "args": [], "result": "Class method" }, { "name": "thisInObjLiteralFuncExpr", "args": [], "result": "ObjLiteral method" } ] }, { "module": "fallback_quickjs", "entries": [ { "name": "mapTest", "args": [], "result": "2\nundefined\nworld\nundefined\ntrue\nfalse\n11\n1\n1\n3" }, { "name": "setTest", "args": [], "result": "true\n2\n[wasm object]\ntrue\ntrue\n4" } ] }, { "module": "fallback_quickjs_JSON", "entries": [ { "name": "JSONTest", "args": [], "result": "42\n{\"result\":true,\"count\":42}\n42\n{\"result\":true,\"count\":42}" }, { "name": "JSONTest2", "args": [], "result": "42\n{\"result\":true,\"count\":42}\n42\n{\"result\":true,\"count\":42}" }, { "name": "JSONTest3", "args": [], "result": "42\n{\"result\":true,\"count\":42}\n{\"''''\\n\\r\\t\\\\1\":\"2\\n\"}" } ] }, { "module": "fallback_quickjs_Date", "entries": [ { "name": "DateTest1", "args": [], "result": "1690416000000\n2023\nInvalid Date\n1995\n2023\n1690416000000\n1692748800000" }, { "name": "DateTest2", "args": [], "result": "1690416000000\n2023\n1690416000000" } ] }, { "module": "array_wasm_basic_type", "entries": [ { "name": "arrayLength", "args": [], "result": "32\n30\n64\n60\n32\n30.3\n64.64\n60.6" } ] }, { "module": "arraybuffer_basic", "entries": [ { "name": "getArrayBufferLength", "args": [], "result": "10\n1" }, { "name": "arrayBufferIsView", "args": [], "result": "false\ntrue" } ] }, { "module": "assign_different_type", "entries": [ { "name": "assignStringArray", "args": [], "result": "hello" }, { "name": "assignClosureArray", "args": [], "result": "100" } ] }, { "module": "auto_box_unbox", "entries": [ { "name": "autoBoxUnboxAssign", "args": [], "result": "1\n2\nstr\nstr1\n10\n11" }, { "name": "autoBoxUnboxObjParam", "args": [], "result": "30\n20" }, { "name": "autoBoxunboxObjField", "args": [], "result": "30\n100\n100" } ] }, { "module": "global_value", "entries": [ { "name": "entry", "args": [], "result": "1\n1" } ] }, { "module": "top_level_statements", "entries": [] }, { "module": "generic_func", "entries": [ { "name": "numberFunc", "args": [], "result": "100" }, { "name": "booleanFunc", "args": [], "result": "true" }, { "name": "stringFunc", "args": [], "result": "hello" }, { "name": "stringArrayFunc", "args": [], "result": "hi" } ] }, { "module": "generic_param", "entries": [ { "name": "testGenericParam", "args": [], "result": "hi\n3" }, { "name": "typeFunc", "args": [], "result": "true\nfalse" } ] }, { "module": "generic_class", "entries": [ { "name": "test_GenericClassWithSingleGenericType", "args": [], "result": "MObjectBase constructor: 0\nMObject constructor: cat\nhello\nMObjectBase constructor: 0\nMObject constructor: 12345\n54321" }, { "name": "test_GenericClassWithMultipleGenericTypes", "args": [], "result": "Generic constructor: hello\nGenericBase1 constructor: true hello\nGenericClass1 constructor: hello 1 true\nworld\n123\nGeneric constructor: 1\nGenericBase1 constructor: 3 1\nGenericClass1 constructor: 1 2 3\n11\n111" }, { "name": "test_GenericClassWithSameBase", "args": [], "result": "Generic constructor: hello\nGenericBase2 constructor: hello\nGenericClass2 constructor: 1 hello\n2\nworld\nGeneric constructor: 11\nGenericBase2 constructor: 11\nGenericClass2 constructor: world 11\nhello\n111" }, { "name": "test_GenericClassWithImplementsInfc", "args": [], "result": "hello\nworld\nworld\n2\n111" }, { "name": "test_GenericClassWithTypeAlias", "args": [], "result": "John\n18\n123\ntrue" }, { "name": "test_AS", "args": [], "result": "1" }, { "name": "test_ClassWithGenericMethod", "args": [], "result": "M.foo: 1\nM.foo: hello\nN.foo: 1\nN.foo: hello\nN.bar: 2\nN.bar: world\nM.foo: false\nN.foo: true" }, { "name": "test_GenericMethodCall", "args": [], "result": "2\nhello" } ] }, { "module": "global_generics_function", "entries": [ { "name": "test", "args": [], "result": "call global function\n2023\ncall global function\nhello world" } ] }, { "module": "inner_generics_function", "entries": [ { "name": "test", "args": [], "result": "call inner function\n2023\ncall inner function\nhello world" } ] }, { "module": "namespace_generics_function", "entries": [ { "name": "test", "args": [], "result": "call namespace function\n2023\ncall namespace function\nhello world" } ] }, { "module": "unary_operator", "entries": [ { "name": "test", "args": [], "result": "4\n4\n4\n5\n0\n2\n4\n4\n4\n3\n2\n0\n1\n0\n-1\n2" } ] }, { "module": "union_assign", "entries": [ { "name": "unionTypeAssign", "args": [], "result": "100:f64" } ] }, { "module": "union_field_get", "entries": [ { "name": "test_func_return_any_null", "args": [], "result": "is null" } ] }, { "module": "union_func_call", "entries": [ { "name": "unionFuncCall", "args": [], "result": "10" } ] }, { "module": "typeof", "entries": [ { "name": "typeofTest", "args": [], "result": "number\nstring\nboolean\nobject\nobject\nobject\nobject\nobject\nundefined\nnumber\nobject\nobject\nnumber\nstring\nfunction\nobject" } ] }, { "module": "toString", "entries": [ { "name": "toStringTest", "args": [], "result": "true\ntrue\ntrue\n16\ntrue\ntrue\ntrue\ntrue\n110\n1[object Object]\n1undefined\n1null\n11,2\n1[wasm Function]\n110\n1[object Object]\nstartmiddle\n2001: A Space Odyssey" } ] }, { "module": "base_function_call", "entries": [ { "name": "test", "args": [], "result": "constructor from Base\nconstructor from A\nconstructor from B\ny: hello\nx: 1\n2\n1\n0\nBase" } ] }, { "module": "non_null_expression", "entries": [ { "name": "test_non_null_func", "args": [], "result": "4:f64" }, { "name": "test_non_null_field", "args": [], "result": "4:f64" }, { "name": "test_non_null_any", "args": [], "result": "4\nundefined" } ] }, { "module": "map_callback", "entries": [ { "name": "map_forEach", "args": [], "result": "0 3 [object Map]\n1 4 [object Map]\n2 5 [object Map]" }, { "name": "map_get", "args": [], "result": "Bob" } ] }, { "module": "percentToken", "entries": [ { "name": "percentToken", "args": [], "result": "1\n-1\n-3\n-3:f64" } ] }, { "module": "tuple", "entries": [ { "name": "tuple_type_with_constant", "args": [], "result": "10\n20.5\nhi" }, { "name": "tuple_type_with_variable", "args": [], "result": "[wasm object]\nhi\n90" }, { "name": "tuple_as_param", "args": [], "result": "30\nhi" }, { "name": "tuple_as_ret", "args": [], "result": "10\n100" }, { "name": "tuple_as_array_elem", "args": [], "result": "hi_1\nhi_2\nhi_3\n1" }, { "name": "tuple_as_obj_field", "args": [], "result": "hi_1\nhi_2\nhi_3" }, { "name": "tuple_with_array", "args": [], "result": "2\nhi_2" }, { "name": "tuple_with_class", "args": [], "result": "1\nhi_2" } ] }, { "module": "typealias", "entries": [ { "name": "useTypeBeforeDefine", "args": [], "result": "A\nA" } ] }, { "module": "rec_types", "entries": [ { "name": "recursiveType1", "args": [], "result": "40:f64" }, { "name": "recursiveType2", "args": [], "result": "50:f64" }, { "name": "defaultFuncUseRecType", "args": [], "result": "run class method\nuse funcType in rec group" } ] }, { "module": "optional_property_access", "entries": [ { "name": "optionalField", "args": [], "result": "str\nstr1\nundefined" }, { "name": "classOptionalField", "args": [], "result": "undefined\n10" }, { "name": "optionalMethod", "args": [], "result": "10\n10\n10\n0" }, { "name": "accessOptFieldOfOptField", "args": [], "result": "undefined\nhello\nhello" }, { "name": "accessOptionalUnionField", "args": [], "result": "14:f64" }, { "name": "accessOptionalUnionField2", "args": [], "result": "true\nundefined\ntrue\n10\n10\n11\n11\n10\n11\n10" }, { "name": "accessOptionalFuncTypedField", "args": [], "result": "undefined" } ] }, { "module": "optional_method", "entries": [ { "name": "optionalMethod", "args": [], "result": "fn is undefined\nfn is bar\nfn is undefined\nfn is undefined" } ] }, { "module": "undefined_test", "entries": [ { "name": "test", "args": [], "result": "undefined\n18 undefined" } ] }, { "module": "array_specialization", "entries": [ { "name": "test", "args": [], "result": "100" } ] }, { "module": "compound_assignment_operator", "entries": [ { "name": "test", "args": [], "result": "3\n8\n5\nhello world" } ] }, { "module": "func_cast_interface", "entries": [ { "name": "test", "args": [], "result": "3\n4\n4\n3\n7" } ] }, { "module": "export_func2", "entries": [ { "name": "defaultFunc", "args": [], "result": "100:f64" }, { "name": "mulFunc", "args": [2,2], "result": "4:f64" }, { "name": "subFunc", "args": [2,2], "result": "0:f64" } ] }, { "module": "mixed_type", "entries": [ { "name": "mixTypeInClass", "args": [], "result": "1\ntrue\n0\n0\n00\n1\ntrue\n0\n0" }, { "name": "mixTypeInInfc", "args": [], "result": "2\ntrue\n-1\n-1\n--1\n2\ntrue\n-1\n-1" } ] }, { "module": "for_in", "entries": [ { "name": "infc_obj_get_field", "args": [], "result": "1\n2" }, { "name": "infc_obj_set_field", "args": [], "result": "100\n100" }, { "name": "extref_obj", "args": [], "result": "1\n88\n2\n88" }, { "name": "dynamic_obj", "args": [], "result": "1\n88\n2\n88" }, { "name": "mix_obj", "args": [], "result": "1\n88\n2\n88\n3\n88" }, { "name": "forInWithContinue", "args": [], "result": "0x1:i32" }, { "name": "infc_obj_get_method", "args": [], "result": "1\n2" }, { "name": "infc_obj_set_method", "args": [], "result": "100\n100" } ] }, { "module": "for_of", "entries": [ { "name": "forOfForArray", "args": [], "result": "1\n4\n6\n1\n4\n6\n1\n4\n6\n2\n3\n5\n2\n3\n5\n2\n3\n5" }, { "name": "forOfForString", "args": [], "result": "a\nb\nc" }, { "name": "forOfForMapKeys", "args": [], "result": "key1\nkey2\nkey1\nkey2\nkey1\nkey2" }, { "name": "forOfForMapValues", "args": [], "result": "value1\nvalue2\nvalue1\nvalue2" }, { "name": "forOfForMapEntries", "args": [], "result": "[key1,value1]\n[key2,value2]\n[key1,value1]\n[key2,value2]" }, { "name": "forOfForSetValues", "args": [], "result": "value1\nvalue2\nvalue1\nvalue2" }, { "name": "forOfWithContinue", "args": [], "result": "0x1:i32" } ] }, { "module": "spread_operator", "entries": [ { "name": "spread_number_array", "args": [], "result": "8\n2\n4\n1\n0\n0\n0" }, { "name": "spread_boolean_array", "args": [], "result": "5\ntrue\nfalse" }, { "name": "spread_string_array", "args": [], "result": "3\n1" }, { "name": "spread_object_array", "args": [], "result": "3\nA1\nA4\nA4" }, { "name": "spread_interface_array", "args": [], "result": "3\nA1" }, { "name": "spread_literal_array", "args": [], "result": "5\n1\n2\n4\n3\n1\n2\n3\nfalse\ntrue\n3\nA2\nA3\n3\n1\n2\n2\n2\n3" }, { "name": "spread_any_array", "args": [], "result": "4\n1\n4\nA1\n7\nA1\n10" }, { "name": "spread_nested_array", "args": [], "result": "3\n2\n4\n5" }, { "name": "pass_spread_to_rest_param", "args": [], "result": "5\n20\n30\n4\n20\n3\n4\nA20\nA30\n3\nA20\nA2\n8\n2\nA3\nA1" } ] }, { "module": "enum", "entries": [ { "name": "assignDigitMember", "args": [], "result": "0" }, { "name": "assignStringMember", "args": [], "result": "red" }, { "name": "enumInitialize", "args": [], "result": "3\n1" }, { "name": "enumParamter", "args": [], "result": "a\nb" } ] }, { "module": "unsigned_value", "entries": [ { "name": "random_f64", "args": [], "result": "0.7322977:f64" }, { "name": "random_i64", "args": [], "result": "0:f64" }, { "name": "random_i32", "args": [], "result": "0:f64" }, { "name": "random_f32", "args": [], "result": "0:f64" } ] }, { "module": "decimalization", "entries": [ { "name": "initNumberWithBinary", "args": [], "result": "5\n10" }, { "name": "initNumberWithOctal", "args": [], "result": "121\n968" }, { "name": "initNumberWithHex", "args": [], "result": "2801\n2800\n2800" } ] }, { "module": "wasmType_basic", "entries": [ { "name": "wasmTypeAssign", "args": [], "result": "132\n164\n232.2525177001953\n264.75757575\nhi" }, { "name": "toI32Value", "args": [], "result": "164\n232\n264" }, { "name": "toI64Value", "args": [], "result": "132\n232\n264" }, { "name": "toF32Value", "args": [], "result": "132\n164\n264.757568359375" }, { "name": "toF64Value", "args": [], "result": "132\n164\n232.32000732421875" }, { "name": "anyConvertValue", "args": [], "result": "hi\n900\n900\n800\n800" }, { "name": "optimizeLiteralValue", "args": [], "result": "100\n100\n100\n100" }, { "name": "operateWasmTypeAndLiteral", "args": [], "result": "110\n110.8\n120\n120.8\n130.5\n131.3\n140.55\n141.35" }, { "name": "operateWasmTypeAndWasmType", "args": [], "result": "20\n30\n40.5\n50.555\n30\n40\n50.5\n60.555\n40.5\n50.5\n61\n71.055\n50.555\n60.555\n71.055\n81.11" }, { "name": "wasmTypeCompare", "args": [], "result": "0" }, { "name": "wasmTypeUnaryExpr", "args": [], "result": "1\n0\nhi" }, { "name": "xor", "args": [], "result": "4656\n-2\n4656" }, { "name": "and", "args": [], "result": "4\n-1\n4" }, { "name": "or", "args": [], "result": "4660\n-1\n4660" }, { "name": "shl", "args": [], "result": "-2\n-2\n8" }, { "name": "shr", "args": [], "result": "1073741823\n1073741824\n-1\n2147483647\n2\n2147483648" } ] }, { "module": "wasmType_heapType", "entries": [ { "name": "wasmArrayTypeWithLiteral", "args": [], "result": "hi\nhello\n88\n77" }, { "name": "wasmArrayTypeWithNewArray", "args": [], "result": "0\n1\n2\n8\n9" }, { "name": "wasmArrayTypeNested", "args": [], "result": "0x1:i64" }, { "name": "wasmArrayTypeInArray", "args": [], "result": "0x1:i64" }, { "name": "wasmArrayTypeInObj", "args": [], "result": "0x1:i64" }, { "name": "wasmStructType", "args": [], "result": "10\n99\n20\n33\n2" }, { "name": "wasmStructTypeNested", "args": [], "result": "0x1:i64" }, { "name": "wasmStructTypeInArray", "args": [], "result": "0x1:i64" }, { "name": "wasmStructTypeInObj", "args": [], "result": "0x1:i64" } ] }, { "module": "wasmType_in_otherType", "entries": [ { "name": "NonEffectWasmType", "args": [], "result": "1" }, { "name": "wasmTypeInClass", "args": [], "result": "1\n2\n3.5\n5.5\n10\n20\n30.5\n40.45" }, { "name": "wasmTypeInObj", "args": [], "result": "1\n2\n3.5\n5.5\n10\n20\n30.5\n40.45" }, { "name": "wasmTypeAs", "args": [], "result": "100\n100\n100.77999877929688\n100.78" }, { "name": "wasmTypeInArray", "args": [], "result": "1\n10\n12\n2\n5\n1\n2.986999988555908\n2\n5.889999866485596\n1\n2.987\n2\n5.89\n2\n3.5\nhi" }, { "name": "wasmTypeI32AsReturnType", "args": [], "result": "0x64:i32" }, { "name": "wasmTypeI64AsReturnType", "args": [], "result": "0x64:i64" }, { "name": "wasmTypeF32AsReturnType", "args": [], "result": "100.25:f32" }, { "name": "wasmTypeF64AsReturnType", "args": [], "result": "100.25:f64" } ] } ] ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { "target": "ES6", "module": "ESNext", "moduleResolution": "Node", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true, "outDir": "build", "sourceMap": true, "allowJs": true, "experimentalDecorators": true, }, "include": ["scripts/*", "src/**/*", "cli/*", "config/*"] } ================================================ FILE: tsconfig.release.json ================================================ { "compilerOptions": { "target": "ES6", "module": "ESNext", "moduleResolution": "Node", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true, "outDir": "build", "sourceMap": false, "allowJs": true, "experimentalDecorators": true, }, "include": ["scripts/*", "src/**/*", "cli/*", "config/*"] }