diff --git a/.gitattributes b/.gitattributes
index 915d1ed51d121f1986c9dfe71cf1745c1a11286d..7d94e0961337f83b1b3f764a7e123a0a999561f8 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,3 +1,4 @@
# Dart sources have to be normalized so that source offsets in kernel files
# with source embedded in them match across all platforms.
-*.dart text eol=lf
\ No newline at end of file
+*.dart text eol=lf
+*.patch* text eol=lf
\ No newline at end of file
diff --git a/BUILD.gn b/BUILD.gn
index 49784eff36e036769a6651ae0af76a16c39286db..50a17bd38d052768fadf424870d8aca29c4c3909 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -39,7 +39,7 @@ config("export_dynamic_symbols") {
# -exported_symbols_list is the macOS linker syntax. The different flags
# accept files formatted differently, so we have exported_symbols.sym for GNU
# linker syntax, and exported_symbols_mac.sym for the macOS linker syntax.
- if (is_linux || is_fuchsia) {
+ if (is_ohos|| is_linux || is_fuchsia) {
inputs = [ "//flutter/common/exported_symbols.sym" ]
ldflags = [ "-Wl,--dynamic-list=" + rebase_path(inputs[0], root_build_dir) ]
} else if (is_mac) {
@@ -129,7 +129,7 @@ group("flutter") {
if (build_engine_artifacts) {
public_deps += [
- "//flutter/shell/testing",
+ # "//flutter/shell/testing($host_toolchain)",
"//flutter/tools/const_finder",
"//flutter/tools/font_subset",
]
@@ -174,6 +174,12 @@ group("unittests") {
]
}
+ if (is_ohos) {
+ public_deps += [
+ "//flutter/shell/platform/ohos:flutter_ohos_unittests",
+ ]
+ }
+
if (is_ios) {
public_deps += [ "//flutter/shell/platform/darwin/ios:ios_test_flutter" ]
}
diff --git a/DEPS b/DEPS
index cec3f632f4a05279325230fa3b9c856799b61394..bcb562b2c87afef9b60449ca58e2b75777a2ca20 100644
--- a/DEPS
+++ b/DEPS
@@ -595,7 +595,7 @@ deps = {
}
],
'dep_type': 'cipd',
- 'condition': 'host_os == "win" and download_dart_sdk'
+ 'condition': 'host_os == "win" and download_dart_sdk and not release_candidate and host_cpu == "arm64"'
},
# esbuild download
@@ -1256,4 +1256,10 @@ hooks = [
'src/flutter/tools/fuchsia/test_scripts/gen_build_defs.py',
],
},
+ {
+ # Generate the ohos compile environment
+ 'name': 'ohos_setup',
+ 'pattern': 'src/flutter/attachment/scripts/.*\\.py',
+ 'action': ['python3', 'src/flutter/attachment/scripts/ohos_setup.py'],
+ }
]
diff --git a/OAT.xml b/OAT.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a1e1a7950dadf713a2dde6ce10a619cc69c1ada6
--- /dev/null
+++ b/OAT.xml
@@ -0,0 +1,157 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.OpenSource b/README.OpenSource
new file mode 100644
index 0000000000000000000000000000000000000000..d0011eeaca00e7fc9a203f1c48151d3c2b060271
--- /dev/null
+++ b/README.OpenSource
@@ -0,0 +1,11 @@
+[
+ {
+ "Name": "engine",
+ "License": "BSD 3-Clause License",
+ "License File": "LICENSE",
+ "Version Number": "3.7.12",
+ "Owner": "aibin@openvalley.net",
+ "Upstream URL": "https://github.com/flutter/engine/",
+ "Description": "The Flutter Engine is a portable runtime for hosting Flutter applications. It implements Flutter's core libraries, including animation and graphics, file and network I/O, accessibility support, plugin architecture, and a Dart runtime and compile toolchain. Most developers will interact with Flutter via the Flutter Framework, which provides a modern, reactive framework, and a rich set of platform, layout and foundation widgets."
+ }
+]
\ No newline at end of file
diff --git a/README.en.md b/README.en.md
new file mode 100644
index 0000000000000000000000000000000000000000..97cf53ade49bb118e0f18787116a55da01370d37
--- /dev/null
+++ b/README.en.md
@@ -0,0 +1,142 @@
+Flutter Engine
+==============
+
+Source of the original repository: https://github.com/flutter/engine
+
+## Repository Description
+This repository is an extension of the Flutter engine repository. It enables Flutter engine to run on OpenHarmony devices.
+
+## How to Build
+
+* Build environment:
+1. Linux or macOS that support Flutter engine; Windows that supports **gen_snapshot**.
+2. Access to the **allowed_hosts** field in the DEPS file.
+
+* Build steps:
+1. Set up a basic environment. For details, see the [official document](https://github.com/flutter/flutter/wiki/Setting-up-the-Engine-development-environment).
+
+ The following libraries need to be installed:
+
+ ```
+ sudo apt install python3
+ sudo apt install pkg-config
+ sudo apt install ninja-build
+ ```
+
+ For Windows:
+ Refer to the section "Compiling for Windows"
+ in the [official document](https://github.com/flutter/flutter/wiki/Compiling-the-engine#compiling-for-windows).
+
+
+2. Configure the file. Specifically, create an empty folder **engine**, create the **.gclient** file in this folder, and edit the file.
+
+ ```
+ solutions = [
+ {
+ "managed": False,
+ "name": "src/flutter",
+ "url": "git@gitee.com:harmonycommando_flutter/flutter_engine.git@oh-3.22.0",
+ "custom_deps": {},
+ "deps_file": "DEPS",
+ "safesync_url": "",
+ },
+ ]
+ ```
+
+3. Synchronize the code. In the **engine** directory, execute `gclient sync`. The engine source code and packages repository will be synchronized, and the **ohos_setup** task will be executed.
+
+4. Download the SDK. Download the supporting development kits from [HarmonyOS SDK](https://developer.huawei.com/consumer/en/develop). Suites downloaded from other platforms are not supported.
+
+ ```sh
+ # Environment variables to set: HarmonyOS SDK, ohpm, hvigor, and node.
+ export TOOL_HOME=/Applications/DevEco-Studio.app/Contents # macOS environment
+ export DEVECO_SDK_HOME=$TOOL_HOME/sdk # command-line-tools/sdk
+ export PATH=$TOOL_HOME/tools/ohpm/bin:$PATH # command-line-tools/ohpm/bin
+ export PATH=$TOOL_HOME/tools/hvigor/bin:$PATH # command-line-tools/ hvigor/bin
+ export PATH=$TOOL_HOME/tools/node/bin:$PATH # command-line-tools/tool/node/bin
+ ```
+
+5. Start building. In the **engine** directory, execute `./ohos` to start building the Flutter engine that supports ohos devices.
+Starting from version 3.22.0, the engine compilation will by default compile both the `local-engine` and `local-host-engine`. When the SDK specifies local compiled artifacts, it must specify both of these compiled artifacts. For example:
+
+ ```shell
+ flutter build hap --target-platform ohos-arm64 --release --local-engine=
/engine/src/out/ohos_release_arm64/ --local-engine-host=/engine/src/out/host_release
+ ```
+
+6. Update the code. In the **engine** directory, execute `./ohos -b master`.
+
+- Manual code update method (after completing at least one full `gclient sync`):
+ * Navigate directly to the `engine/src/flutter` directory. After ensuring that the current code update does not involve any patches, you may freely edit or switch code branches, and then execute the build command. If patches are involved, you can manually apply the relevant patches or refer to FAQ.7 to batch reverse and reapply patches.
+
+
+## FAQs
+1. The message `Member notfound:'isOhos'` is reported during project running.
Install all dart patches in the **src/third_party/dart** directory. (The patches are located in the **src/flutter/attachment/repos** directory, and you can use **git apply** to apply the patches). Recompile the engine after installing the patches.
+
+2. The message `Permission denied` is reported.
Execute `chmod +x < script file >` to add the execution permission.
+
+3. To compile the engine in debug, release, or profile mode, execute `./ohos -t debug`, `./ohos -t release`, or `./ohos -t profile`, respectively.
+
+4. To find the help, execute `./ohos -h`.
+
+5. Different ways for handling newline characters by Windows, macOS, and Linux will cause different results of **dart vm snapshot hash** when installing the dart patches. You can obtain the value of **snapshot hash** through the following method:
+
+ ```shell
+ python xxx/src/third_party/dart/tools/make_version.py --format='{{SNAPSHOT_HASH}}'
+ ```
+
+ Here, **xxx** is the engine path you created.
+
+ If the obtained value is not **8af474944053df1f0a3be6e6165fa7cf**, check whether all lines of the **xxx/src/third_party/dart/runtime/vm/dart.cc** file and the **xxx/src/third_party/dart/runtime/vm/image_snapshot.cc** file end with **LF**. For Windows, you can use **Notepad++** to check; for other systems, consult specific methods on your own.
+
+6. After modifying the embedding layer code, running `./ohos` does not take effect. You need to modify the timestamp of any file in the C++ layer for the build system to recognize it (e.g., add a space in any C++ file, save it, then remove the space and save again). Re-run the build to trigger the embedding layer to be repackaged.
+
+7. Method to manually reapply patches:
+
+ ```shell
+ python engine/src/flutter/attachment/scripts/ohos_reverse_patch.py
+ python engine/src/flutter/attachment/scripts/ohos_setup.py
+ ```
+
+8. Configure automatic code navigation
+
+ It is recommended to use the `vscode+clangd` plugin (note: it conflicts with C/C++ IntelliSense).
+ Example configuration:
+ ```json
+ "clangd.path": "./src/flutter/buildtools/linux-x64/clang/bin/clangd",
+ "clangd.arguments": [
+ "--compile-commands-dir=./src/out/ohos_release_arm64/",
+ "--query-driver=./src/flutter/buildtools/linux-x64/clang/bin/clang++"
+ ],
+ "C_Cpp.intelliSenseEngine": "disabled",
+ ```
+
+9. `gclient sync` error on Windows
+
+ Key error message: `'A required privilege is not held by the client'`
+
+ Solution: Go to Settings -> Developer Options -> Enable "Developer Mode"
+
+
+## Code Building at the Embedding Layer
+
+1. Edit **shell/platform/ohos/flutter_embedding/local.properties** as follows:
+
+ ```
+ sdk.dir=
+ nodejs.dir=
+ ```
+
+2. Copy the file to `shell/platform/ohos/flutter_embedding/flutter/libs/arm64-v8a/` from the built `engine` directory.
+ 1. For the debug or release version, copy `libflutter.so`.
+ 2. For the profile version, copy `libflutter.so` and `libvmservice_snapshot.so`.
+
+3. In the **shell/platform/ohos/flutter_embedding** directory, execute the following instruction:
+
+ ```
+ # buildMode can be set to debug, release, or profile.
+ hvigorw --mode module -p module=flutter@default -p product=default -p buildMode=debug assembleHar --no-daemon
+ ```
+
+4. Find the HAR file from the path `shell/platform/ohos/flutter_embedding/flutter/build/default/outputs/default/flutter.har`.
+
+If you are using DevEco Studio of a Beta version and encounter the error message `must have required property 'compatibleSdkVersion', location: build-profile.json5:17:11` when building the project, refer to the section "Configuring Plugins" in chapter 6 "Creating a Project and Runing Hello World" of *DevEco Studio Environment Setup.docx* to modify the **shell/platform/ohos/flutter_embedding/hvigor/hvigor-config.json5** file.
diff --git a/README.md b/README.md
index 670c65921ad6cf97ed880662e6bd5f02f11244a5..d810180d87e7076ce9fe74415c60dc3a27ec7337 100644
--- a/README.md
+++ b/README.md
@@ -1,32 +1,155 @@
# Flutter Engine
-[](https://flutter-dashboard.appspot.com/#/build?repo=engine)
-[](https://deps.dev/project/github/flutter%2Fengine)
-[](https://slsa.dev)
-
-Flutter is Google's SDK for crafting beautiful, fast user experiences for
-mobile, web, and desktop from a single codebase. Flutter works with existing
-code, is used by developers and organizations around the world, and is free
-and open source.
-
-The Flutter Engine is a portable runtime for hosting
-[Flutter](https://flutter.dev) applications. It implements Flutter's core
-libraries, including animation and graphics, file and network I/O,
-accessibility support, plugin architecture, and a Dart runtime and compile
-toolchain. Most developers will interact with Flutter via the [Flutter
-Framework](https://github.com/flutter/flutter), which provides a modern,
-reactive framework, and a rich set of platform, layout and foundation widgets.
-
-If you want to run/contribute to Flutter Web engine, more tooling can be
-found at [felt](https://github.com/flutter/engine/tree/main/lib/web_ui#using-felt).
-This is a tool written to make web engine development experience easy.
-
-If you are new to Flutter, then you will find more general information
-on the Flutter project, including tutorials and samples, on our Web
-site at [Flutter.dev](https://flutter.dev). For specific information
-about Flutter's APIs, consider our API reference which can be found at
-the [docs.flutter.dev](https://docs.flutter.dev/).
-
-Flutter is a fully open source project, and we welcome contributions.
-Information on how to get started can be found at our
-[contributor guide](CONTRIBUTING.md).
+原始仓来源:https://github.com/flutter/engine
+
+## 仓库说明:
+本仓库是基于flutter官方engine仓库拓展,可构建支持在OpenHarmony设备上运行的flutter engine程序。
+
+## 构建说明:
+
+* 构建环境:
+1. 目前支持在Linux与MacOS中构建,Windows环境中支持构建gen_snapshot;
+2. 请确保当前构建环境可以访问 `DEPS` 配置文件中 `allowed_hosts` 字段的URL列表。
+
+* 构建步骤:
+1. 构建基础环境:可参照[官网](https://github.com/flutter/flutter/wiki/Setting-up-the-Engine-development-environment);
+
+ a) 需要安装的工具: `git`, `curl` and `unzip`
+
+ b) 克隆 `gclient` 与 `gn` 构建工具的代码仓库
+
+ ```
+ git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
+ ```
+
+ 添加 `depot_tools` 到 `PATH` 环境变量中
+
+ ```
+ export PATH=/home//depot_tools:$PATH
+ ```
+
+ c) 需要安装的基础库:
+
+ ```
+ sudo apt install python3
+ sudo apt install pkg-config
+ sudo apt install ninja-build
+ ```
+
+ Windows构建环境:
+ 可参考[官网](https://github.com/flutter/flutter/wiki/Compiling-the-engine#compiling-for-windows)
+ "Compiling for Windows" 章节搭建Windows构建环境
+
+
+2. 配置文件:
+
+ a) 创建名为 `engine` 的空目录
+
+ b) 在 `engine` 目录内新建 `.gclient` 文件
+
+ c) 编辑 `.gclient` 文件:
+ ```
+ solutions = [
+ {
+ "managed": False,
+ "name": "src/flutter",
+ "url": "git@gitee.com:harmonycommando_flutter/flutter_engine.git@oh-3.22.0",
+ "custom_deps": {},
+ "deps_file": "DEPS",
+ "safesync_url": "",
+ },
+ ]
+ ```
+
+3. 同步代码:在 `engine` 目录中执行 `gclient sync` 命令;这里会同步engine源码、官方packages仓,还有执行ohos_setup任务;
+
+4. 下载sdk: 从[鸿蒙SDK](https://developer.huawei.com/consumer/cn/develop)下载配套开发工具,暂不支持非该渠道下载的套件
+
+ ```sh
+ # 需要设置的环境变量: HarmonyOS SDK, ohpm, hvigor, node
+ export TOOL_HOME=/Applications/DevEco-Studio.app/Contents # mac环境
+ export DEVECO_SDK_HOME=$TOOL_HOME/sdk # command-line-tools/sdk
+ export PATH=$TOOL_HOME/tools/ohpm/bin:$PATH # command-line-tools/ohpm/bin
+ export PATH=$TOOL_HOME/tools/hvigor/bin:$PATH # command-line-tools/ hvigor/bin
+ export PATH=$TOOL_HOME/tools/node/bin:$PATH # command-line-tools/tool/node/bin
+ ```
+
+5. 开始构建:在 `engine` 目录,执行`./ohos`,即可开始构建支持ohos设备的flutter engine。
+ 从3.22.0版本开始,engine编译会默认编译local-engine以及local-host-engine, sdk在指定本地编译产物时需要同时指定这两个编译产物,例如:
+ ```shell
+ flutter build hap --target-platform ohos-arm64 --release --local-engine=/engine/src/out/ohos_release_arm64/ --local-engine-host=//engine/src/out/host_release
+ ```
+
+6. 更新代码:在 `engine` 目录,执行`./ohos -b master`
+
+- 手动更新代码方法(至少经过一次完整gclient sync之后):
+ * 直接进入engine/src/flutter目录,在确保当前更新代码未涉及patch后,可以自由编辑或切换代码分支,然后执行编译命令,如若涉及patch修改,可手动apply相关patch或参考FAQ.7 批量reverse后重新apply patch
+
+## FAQ
+1. 运行项目工程报 `Member notfound:'isOhos'` 的错误:请确保src/third_party/dart目录下应用了所有的dart patch(补丁位于src/flutter/attachment/repos目录,可使用git apply应用patch)应用patch后重新编译engine
+
+2. 提示`Permission denied:` 执行 `chmod +x <脚本文件>` 添加执行权限
+
+3. 单独编译 `debug/release/profile` 模式的engine:`./ohos -t debug|release|profile`
+
+4. 查看帮助:`./ohos -h`
+
+5. 由于Windows和MacOS、Linux对换行符处理方式不同,在应用dart补丁时会造成dart vm snapshot hash结果不同,可通过以下方法获取当前snapshot hash值
+
+ ```shell
+ python engine/src/third_party/dart/tools/make_version.py --format='{{SNAPSHOT_HASH}}'
+ ```
+
+ 如果获取到的值不是“8af474944053df1f0a3be6e6165fa7cf”那么就需要检查 `engine/src/third_party/dart/runtime/vm/dart.cc` 文件和 `engine/src/third_party/dart/runtime/vm/image_snapshot.cc` 文件中全部行的结尾是不是以LF结尾的,Windows可以使用notepad++查看,其它系统具体方法请自行查询
+
+6. 修改了embedding层代码,运行./ohos没有生效,需要将任意cpp层文件修改一下时间戳,编译系统才可以识别(例如任意cpp文件添加空格保存后再删除空格后保存),重新执行则可以触发embedding层重新打包
+
+7. 手动重新应用patch的方法:
+
+ ```shell
+ python engine/src/flutter/attachment/scripts/ohos_reverse_patch.py
+ python engine/src/flutter/attachment/scripts/ohos_setup.py
+ ```
+
+8. 配置代码自动跳转
+
+ 推荐使用vscode+clangd(与C/C++ IntelliSense有冲突)插件
+ 可参考配置:
+ ```json
+ "clangd.path": "./src/flutter/buildtools/linux-x64/clang/bin/clangd",
+ "clangd.arguments": [
+ "--compile-commands-dir=./src/out/ohos_release_arm64/",
+ "--query-driver=./src/flutter/buildtools/linux-x64/clang/bin/clang++",
+ ],
+ "C_Cpp.intelliSenseEngine": "disabled",
+ ```
+
+9. Windows下gclient sync报错
+
+ 关键报错信息'A required privilege is not held by the client'
+
+ 解决方案:设置->开发者选项->开启"开发人员模式"
+
+## embedding层代码构建指导
+
+1. 编辑 `shell/platform/ohos/flutter_embedding/local.properties`:
+
+ ```
+ sdk.dir=
+ nodejs.dir=
+ ```
+
+2. 你需要复制文件到 `shell/platform/ohos/flutter_embedding/flutter/libs/arm64-v8a/`
+ 1. `debug/release`,复制 `libflutter.so`
+ 2. `profile`,复制 `libflutter.so` 和 `libvmservice_snapshot.so`
+
+3. 在 `shell/platform/ohos/flutter_embedding` 目录下,执行
+
+ ```
+ # buildMode可选值为: debug release profile
+ hvigorw --mode module -p module=flutter@default -p product=default -p buildMode=debug assembleHar --no-daemon
+ ```
+
+4. `har` 文件输出路径为:`shell/platform/ohos/flutter_embedding/flutter/build/default/outputs/default/flutter.har`
+
+ps:如果你使用的是DevEco Studio的Beta版本,编译工程时遇到“must have required property 'compatibleSdkVersion', location: build-profile.json5:17:11"错误,请参考《DevEco Studio环境配置指导.docx》中的‘6 创建工程和运行Hello World’【配置插件】章节修改 `shell/platform/ohos/flutter_embedding/hvigor/hvigor-config.json5` 文件。
diff --git a/assets/asset_resolver.h b/assets/asset_resolver.h
index f8ec95ece6797566a12b041afc8ed75604df6da9..0b1212f414dc0395963649b719fe83861ba196a0 100644
--- a/assets/asset_resolver.h
+++ b/assets/asset_resolver.h
@@ -17,6 +17,7 @@ namespace flutter {
class AssetManager;
class APKAssetProvider;
class DirectoryAssetBundle;
+class OHOSAssetProvider;
class AssetResolver {
public:
@@ -40,6 +41,9 @@ class AssetResolver {
virtual const DirectoryAssetBundle* as_directory_asset_bundle() const {
return nullptr;
}
+ virtual const OHOSAssetProvider* as_ohos_asset_provider() const {
+ return nullptr;
+ }
virtual bool IsValid() const = 0;
diff --git a/attachment/repos/angle-3.22.patch b/attachment/repos/angle-3.22.patch
new file mode 100644
index 0000000000000000000000000000000000000000..13360322e2e37ec28f82678845ebae15431df072
--- /dev/null
+++ b/attachment/repos/angle-3.22.patch
@@ -0,0 +1,112 @@
+diff --git a/BUILD.gn b/BUILD.gn
+index 621a97bfb..7e9bb8964 100644
+--- a/BUILD.gn
++++ b/BUILD.gn
+@@ -103,7 +103,7 @@ if (angle_build_all) {
+ ":translator_fuzzer",
+ ":xxhash_fuzzer",
+ "$angle_root/samples:angle_samples",
+- "$angle_root/src/tests:angle_tests",
++ #"$angle_root/src/tests:angle_tests",
+ ]
+ if (angle_enable_cl) {
+ deps += [ "$angle_root/src/libOpenCL:angle_cl" ]
+@@ -559,7 +559,8 @@ angle_static_library("angle_image_util") {
+ sources = libangle_image_util_sources
+ public_configs += [ ":angle_image_util_config" ]
+ public_deps = [ ":angle_image_util_headers" ]
+-
++
++ angle_has_astc_encoder = false
+ if (angle_has_astc_encoder) {
+ public_deps += [ "third_party/astc-encoder:astcenc" ]
+ include_dirs = [ "third_party/astc-encoder/src/Source/" ]
+diff --git a/gni/angle.gni b/gni/angle.gni
+index 31fb784ed..8f5975ec5 100644
+--- a/gni/angle.gni
++++ b/gni/angle.gni
+@@ -63,7 +63,7 @@ if (angle_has_build) {
+
+ declare_args() {
+ angle_use_gbm = ozone_platform_gbm
+- angle_use_x11 = ozone_platform_x11 && !is_ggp && (is_linux || is_chromeos)
++ angle_use_x11 = ozone_platform_x11 && !is_ggp && (is_linux || is_chromeos) && !is_castos
+ angle_use_wayland =
+ ozone_platform_wayland && !is_ggp && is_linux && !is_castos
+ angle_use_vulkan_display = (is_linux || is_chromeos) && !is_ggp
+@@ -197,7 +197,9 @@ declare_args() {
+
+ declare_args() {
+ # ASTC emulation is only built on standalone non-android builds
+- angle_has_astc_encoder = angle_has_build && angle_standalone && !is_android
++ #angle_has_astc_encoder = angle_has_build && angle_standalone && !is_android
++ #for ohos
++ angle_has_astc_encoder = false
+ }
+
+ declare_args() {
+@@ -266,13 +268,13 @@ declare_args() {
+ angle_wayland_dir = "$angle_root/third_party/wayland"
+
+ angle_vulkan_headers_dir =
+- "$angle_root/third_party/vulkan-deps/vulkan-headers/src"
++ "//flutter/third_party/vulkan-deps/vulkan-headers/src"
+ angle_vulkan_loader_dir =
+- "$angle_root/third_party/vulkan-deps/vulkan-loader/src"
++ "//flutter/third_party/vulkan-deps/vulkan-loader/src"
+ angle_vulkan_tools_dir =
+- "$angle_root/third_party/vulkan-deps/vulkan-tools/src"
++ "//flutter/third_party/vulkan-deps/vulkan-tools/src"
+ angle_vulkan_validation_layers_dir =
+- "$angle_root/third_party/vulkan-deps/vulkan-validation-layers/src"
++ "//flutter/third_party/vulkan-deps/vulkan-validation-layers/src"
+
+ angle_build_vulkan_system_info = angle_has_build && !angle_is_winuwp
+
+diff --git a/include/EGL/eglplatform.h b/include/EGL/eglplatform.h
+index 777e98558..23348f47a 100644
+--- a/include/EGL/eglplatform.h
++++ b/include/EGL/eglplatform.h
+@@ -111,13 +111,19 @@ typedef intptr_t EGLNativeWindowType;
+
+ #elif defined(USE_X11)
+
+-/* X11 (tentative) */
+-#include
+-#include
++struct NativeWindow;
+
+-typedef Display *EGLNativeDisplayType;
+-typedef Pixmap EGLNativePixmapType;
+-typedef Window EGLNativeWindowType;
++typedef void* EGLNativeDisplayType;
++typedef void* EGLNativePixmapType;
++typedef struct NativeWindow* EGLNativeWindowType;
++
++// /* X11 (tentative) */
++// #include
++// #include
++
++// typedef Display *EGLNativeDisplayType;
++// typedef Pixmap EGLNativePixmapType;
++// typedef Window EGLNativeWindowType;
+
+ #elif defined(__unix__)
+
+diff --git a/src/common/angle_version.h b/src/common/angle_version.h
+index 488c65dfe..9a0b295d9 100644
+--- a/src/common/angle_version.h
++++ b/src/common/angle_version.h
+@@ -15,8 +15,12 @@
+ #define ANGLE_MINOR_VERSION 1
+
+ #ifndef ANGLE_REVISION
++#ifndef ANGLE_COMMIT_POSITION
++# define ANGLE_REVISION 0
++#else
+ # define ANGLE_REVISION ANGLE_COMMIT_POSITION
+ #endif
++#endif
+
+ #define ANGLE_STRINGIFY(x) #x
+ #define ANGLE_MACRO_STRINGIFY(x) ANGLE_STRINGIFY(x)
diff --git a/attachment/repos/angle.patch b/attachment/repos/angle.patch
new file mode 100644
index 0000000000000000000000000000000000000000..e4a0952dd4dce8a288dd5aaead27e49bc803628b
--- /dev/null
+++ b/attachment/repos/angle.patch
@@ -0,0 +1,126 @@
+diff --git a/BUILD.gn b/BUILD.gn
+index efaf09033..73af49870 100644
+--- a/BUILD.gn
++++ b/BUILD.gn
+@@ -83,7 +83,7 @@ if (angle_build_all) {
+ ":translator_fuzzer",
+ ":xxhash_fuzzer",
+ "$angle_root/samples:angle_samples",
+- "$angle_root/src/tests:angle_tests",
++ #"$angle_root/src/tests:angle_tests",
+ ]
+ if (angle_enable_cl) {
+ deps += [ "$angle_root/src/libOpenCL:angle_cl" ]
+@@ -486,7 +486,8 @@ angle_static_library("angle_image_util") {
+ sources = libangle_image_util_sources
+ public_configs += [ ":angle_image_util_config" ]
+ public_deps = [ ":angle_image_util_headers" ]
+-
++
++ angle_has_astc_encoder = false
+ if (angle_has_astc_encoder) {
+ public_deps += [ "third_party/astc-encoder:astcenc" ]
+ include_dirs = [ "third_party/astc-encoder/src/Source/" ]
+diff --git a/gni/angle.gni b/gni/angle.gni
+index b8086660a..d82d056d6 100644
+--- a/gni/angle.gni
++++ b/gni/angle.gni
+@@ -60,8 +60,7 @@ if (angle_has_build) {
+
+ declare_args() {
+ angle_use_gbm = ozone_platform_gbm
+- angle_use_x11 =
+- ozone_platform_x11 && !is_ggp && (is_linux || is_chromeos) && !is_castos
++ angle_use_x11 = ozone_platform_x11 && !is_ggp && (is_linux || is_chromeos) && !is_castos
+ angle_use_wayland =
+ ozone_platform_wayland && !is_ggp && is_linux && !is_castos
+ angle_use_vulkan_display = (is_linux || is_chromeos) && !is_ggp
+@@ -191,7 +190,9 @@ declare_args() {
+
+ declare_args() {
+ # ASTC emulation is only built on standalone non-android builds
+- angle_has_astc_encoder = angle_has_build && angle_standalone && !is_android
++ #angle_has_astc_encoder = angle_has_build && angle_standalone && !is_android
++ #for ohos
++ angle_has_astc_encoder = false
+ }
+
+ declare_args() {
+@@ -255,13 +256,13 @@ declare_args() {
+ angle_wayland_dir = "$angle_root/third_party/wayland"
+
+ angle_vulkan_headers_dir =
+- "$angle_root/third_party/vulkan-deps/vulkan-headers/src"
++ "//third_party/vulkan-deps/vulkan-headers/src"
+ angle_vulkan_loader_dir =
+- "$angle_root/third_party/vulkan-deps/vulkan-loader/src"
++ "//third_party/vulkan-deps/vulkan-loader/src"
+ angle_vulkan_tools_dir =
+- "$angle_root/third_party/vulkan-deps/vulkan-tools/src"
++ "//third_party/vulkan-deps/vulkan-tools/src"
+ angle_vulkan_validation_layers_dir =
+- "$angle_root/third_party/vulkan-deps/vulkan-validation-layers/src"
++ "//third_party/vulkan-deps/vulkan-validation-layers/src"
+
+ angle_build_vulkan_system_info = angle_has_build && !angle_is_winuwp
+
+diff --git a/include/EGL/eglplatform.h b/include/EGL/eglplatform.h
+index 9ebaf00a9..fe111d115 100644
+--- a/include/EGL/eglplatform.h
++++ b/include/EGL/eglplatform.h
+@@ -117,13 +117,19 @@ typedef khronos_uintptr_t EGLNativeWindowType;
+
+ #elif defined(__unix__) || defined(USE_X11)
+
+-/* X11 (tentative) */
+-#include
+-#include
++struct NativeWindow;
+
+-typedef Display *EGLNativeDisplayType;
+-typedef Pixmap EGLNativePixmapType;
+-typedef Window EGLNativeWindowType;
++typedef void* EGLNativeDisplayType;
++typedef void* EGLNativePixmapType;
++typedef struct NativeWindow* EGLNativeWindowType;
++
++// /* X11 (tentative) */
++// #include
++// #include
++
++// typedef Display *EGLNativeDisplayType;
++// typedef Pixmap EGLNativePixmapType;
++// typedef Window EGLNativeWindowType;
+
+ #elif defined(__APPLE__)
+
+diff --git a/src/common/angle_version.h b/src/common/angle_version.h
+index d9d7e8929..a97e3844b 100644
+--- a/src/common/angle_version.h
++++ b/src/common/angle_version.h
+@@ -14,8 +14,12 @@
+ #define ANGLE_MINOR_VERSION 1
+
+ #ifndef ANGLE_REVISION
++#ifndef ANGLE_COMMIT_POSITION
++# define ANGLE_REVISION 0
++#else
+ # define ANGLE_REVISION ANGLE_COMMIT_POSITION
+ #endif
++#endif
+
+ #define ANGLE_STRINGIFY(x) #x
+ #define ANGLE_MACRO_STRINGIFY(x) ANGLE_STRINGIFY(x)
+diff --git a/src/compiler/translator/BuildSPIRV.h b/src/compiler/translator/BuildSPIRV.h
+index d67bd812b..00d22d025 100644
+--- a/src/compiler/translator/BuildSPIRV.h
++++ b/src/compiler/translator/BuildSPIRV.h
+@@ -110,6 +110,8 @@ struct SpirvType
+ SpirvTypeSpec typeSpec;
+ };
+
++bool operator==(const SpirvType &a, const SpirvType &b);
++
+ struct SpirvIdAndIdList
+ {
+ spirv::IdRef id;
diff --git a/attachment/repos/bootstrap/ohos b/attachment/repos/bootstrap/ohos
new file mode 100755
index 0000000000000000000000000000000000000000..99f75119643b6c9f0b965cccd7f0b02f5a1b79b9
--- /dev/null
+++ b/attachment/repos/bootstrap/ohos
@@ -0,0 +1,18 @@
+#! /bin/bash
+# Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#编译依赖
+#sudo apt install g++-multilib git python3 curl
+
+python3 ./src/flutter/attachment/scripts/ohos.py $@
diff --git a/attachment/repos/bootstrap/ohos.bat b/attachment/repos/bootstrap/ohos.bat
new file mode 100644
index 0000000000000000000000000000000000000000..289c3c0dab13217aaf53a5feeb1edc2377d5004c
--- /dev/null
+++ b/attachment/repos/bootstrap/ohos.bat
@@ -0,0 +1,17 @@
+@rem Copyright (c) 2021-2023 Huawei Device Co., Ltd.
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem http://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+
+@echo off
+setlocal EnableDelayedExpansion
+
+python3 .\src\flutter\attachment\scripts\ohos.py %*
diff --git a/attachment/repos/bootstrap/setup.sh b/attachment/repos/bootstrap/setup.sh
new file mode 100755
index 0000000000000000000000000000000000000000..285760b851a0d9b5cb243e9597f739d7e0c4ac0e
--- /dev/null
+++ b/attachment/repos/bootstrap/setup.sh
@@ -0,0 +1,35 @@
+# Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#! /bin/sh
+
+chmod -R +w .vpython*
+#cp -a .vpython* ~/
+
+cp -a .vpython-root .vpython-root-new
+find .vpython-root-new/ -type l -exec ls -l {} \; | awk '{
+ D = ENVIRON["PWD"];
+ TARGET = $9;
+ s = index($11, "depot");
+ T = D "/" substr($11, s);
+ #print $9 "->" T;
+ cmd = "rm -f " TARGET;
+ system(cmd);
+ cmd = "ln -sf " T " " TARGET;
+ system(cmd);
+}'
+
+rm -rf ~/.vpython-root
+cp -a .vpython-root-new ~/.vpython-root&& rm -rf .vpython-root-new
+cp -a .vpython_cipd_cache ~/
+
+chmod -R +w ~/.vpython*
diff --git a/attachment/repos/boringssl-3.22.patch b/attachment/repos/boringssl-3.22.patch
new file mode 100644
index 0000000000000000000000000000000000000000..1b5d1e65f956d81502bfb6ece49fb58d6b8f7ac7
--- /dev/null
+++ b/attachment/repos/boringssl-3.22.patch
@@ -0,0 +1,21 @@
+diff --git a/BUILD.gn b/BUILD.gn
+index 1ee6799..684f028 100644
+--- a/BUILD.gn
++++ b/BUILD.gn
+@@ -50,6 +50,7 @@ config("no_asm_config") {
+
+ # This has no sources on some platforms so must be a source_set.
+ source_set("boringssl_asm") {
++
+ visibility = [ ":*" ] # Only targets in this file can depend on this.
+
+ defines = []
+@@ -57,7 +58,7 @@ source_set("boringssl_asm") {
+ include_dirs = [ "src/include" ]
+ asmflags = []
+
+- if ((current_cpu == "arm" || current_cpu == "arm64") && is_android &&
++ if ((current_cpu == "arm" || current_cpu == "arm64") && (is_android || is_ohos) &&
+ is_clang) {
+ if (!bssl_use_clang_integrated_as) {
+ # Disable the integrated assembler and use the one shipped with the NDK.
diff --git a/attachment/repos/boringssl.patch b/attachment/repos/boringssl.patch
new file mode 100644
index 0000000000000000000000000000000000000000..21821bef89ffe925b5c80a150467c091dbb17a55
--- /dev/null
+++ b/attachment/repos/boringssl.patch
@@ -0,0 +1,39 @@
+diff --git a/BUILD.gn b/BUILD.gn
+index 670224d..3f88e4e 100644
+--- a/BUILD.gn
++++ b/BUILD.gn
+@@ -49,6 +49,7 @@ config("no_asm_config") {
+
+ # This has no sources on some platforms so must be a source_set.
+ source_set("boringssl_asm") {
++
+ visibility = [ ":*" ] # Only targets in this file can depend on this.
+
+ defines = []
+@@ -76,7 +77,7 @@ source_set("boringssl_asm") {
+ } else if (current_cpu == "x64") {
+ if (is_mac) {
+ sources += crypto_sources_mac_x86_64
+- } else if (is_linux || is_android) {
++ } else if (is_linux || is_android || is_ohos) {
+ sources += crypto_sources_linux_x86_64
+ } else {
+ public_configs = [ ":no_asm_config" ]
+@@ -84,14 +85,14 @@ source_set("boringssl_asm") {
+ } else if (current_cpu == "x86") {
+ if (is_mac) {
+ sources += crypto_sources_mac_x86
+- } else if (is_linux || is_android) {
++ } else if (is_linux || is_android || is_ohos) {
+ sources += crypto_sources_linux_x86
+ } else {
+ public_configs = [ ":no_asm_config" ]
+ }
+- } else if (current_cpu == "arm" && (is_linux || is_android)) {
++ } else if (current_cpu == "arm" && (is_linux || is_android || is_ohos)) {
+ sources += crypto_sources_linux_arm
+- } else if (current_cpu == "arm64" && (is_linux || is_android)) {
++ } else if (current_cpu == "arm64" && (is_linux || is_android || is_ohos)) {
+ sources += crypto_sources_linux_aarch64
+ asmflags += [ "-march=armv8-a+crypto" ]
+ } else {
diff --git a/attachment/repos/build-3.22.patch b/attachment/repos/build-3.22.patch
new file mode 100644
index 0000000000000000000000000000000000000000..c5aac94dbde07570b12fd9a9e3dc89960a85c081
--- /dev/null
+++ b/attachment/repos/build-3.22.patch
@@ -0,0 +1,991 @@
+diff --git a/build/config/BUILD.gn b/build/config/BUILD.gn
+index 4e62965..ea49a53 100644
+--- a/build/config/BUILD.gn
++++ b/build/config/BUILD.gn
+@@ -146,6 +146,8 @@ config("default_libs") {
+ "dl",
+ "m",
+ ]
++ } else if (is_ohos) {
++ libs = [ "dl","hilog_ndk.z" ]
+ } else if (is_linux) {
+ libs = [ "dl" ]
+ }
+diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn
+index 87ea4c5..2dc5843 100644
+--- a/build/config/BUILDCONFIG.gn
++++ b/build/config/BUILDCONFIG.gn
+@@ -194,6 +194,7 @@ if (current_os == "win") {
+ is_fuchsia_host = false
+ is_ios = false
+ is_linux = false
++ is_ohos = false
+ is_mac = false
+ is_posix = false
+ is_win = true
+@@ -202,6 +203,7 @@ if (current_os == "win") {
+ is_android = false
+ is_chromeos = false
+ is_fuchsia = false
++ is_ohos = false
+ is_fuchsia_host = false
+ is_ios = false
+ is_linux = false
+@@ -216,6 +218,7 @@ if (current_os == "win") {
+ is_fuchsia_host = false
+ is_ios = false
+ is_linux = false
++ is_ohos = false
+ is_mac = false
+ is_posix = true
+ is_win = false
+@@ -227,6 +230,7 @@ if (current_os == "win") {
+ is_fuchsia_host = false
+ is_ios = false
+ is_linux = true
++ is_ohos = false
+ is_mac = false
+ is_posix = true
+ is_win = false
+@@ -241,6 +245,7 @@ if (current_os == "win") {
+ is_fuchsia_host = false
+ is_ios = false
+ is_linux = false
++ is_ohos = false
+ is_mac = false
+ is_posix = true
+ is_win = false
+@@ -252,6 +257,19 @@ if (current_os == "win") {
+ is_fuchsia_host = false
+ is_ios = true
+ is_linux = false
++ is_ohos = false
++ is_mac = false
++ is_posix = true
++ is_win = false
++ is_wasm = false
++} else if (current_os == "ohos") {
++ is_android = false
++ is_chromeos = false
++ is_fuchsia = false
++ is_fuchsia_host = false
++ is_ios = false
++ is_linux = false
++ is_ohos = true
+ is_mac = false
+ is_posix = true
+ is_win = false
+@@ -263,6 +281,7 @@ if (current_os == "win") {
+ is_fuchsia_host = false
+ is_ios = false
+ is_linux = true
++ is_ohos = false
+ is_mac = false
+ is_posix = true
+ is_win = false
+@@ -274,6 +293,7 @@ if (current_os == "win") {
+ is_fuchsia_host = false
+ is_ios = false
+ is_linux = false
++ is_ohos = false
+ is_mac = false
+ is_posix = true
+ is_win = false
+@@ -285,6 +305,7 @@ if (current_os == "win") {
+ is_fuchsia_host = false
+ is_ios = false
+ is_linux = false
++ is_ohos = false
+ is_mac = false
+ is_posix = false
+ is_win = false
+@@ -324,7 +345,7 @@ if (!is_clang && using_sanitizer) {
+ is_clang = true
+ }
+
+-use_flutter_cxx = is_clang && (is_linux || is_android || is_mac || is_ios)
++use_flutter_cxx = is_clang && (is_linux || is_android || is_mac || is_ios || is_ohos )
+
+ if (is_msan && !is_linux) {
+ assert(false, "Memory sanitizer is only available on Linux.")
+@@ -376,7 +397,9 @@ if (is_posix) {
+ ]
+ }
+
+-if (is_linux) {
++if (is_ohos || (is_linux && host_os == "mac")) {
++ _native_compiler_configs += [ "//build/config/ohos:sdk" ]
++} else if (is_linux) {
+ _native_compiler_configs += [ "//build/config/linux:sdk" ]
+ } else if (is_mac) {
+ _native_compiler_configs += [ "//build/config/mac:sdk" ]
+@@ -523,8 +546,18 @@ shlib_toolchain = false
+ if (custom_toolchain != "") {
+ assert(custom_sysroot != "")
+ assert(custom_target_triple != "")
+- host_toolchain = "//build/toolchain/linux:clang_$host_cpu"
+ set_default_toolchain("//build/toolchain/custom")
++ if (host_os == "linux") {
++ if (is_clang) {
++ host_toolchain = "//build/toolchain/linux:clang_$host_cpu"
++ } else {
++ host_toolchain = "//build/toolchain/linux:$host_cpu"
++ }
++ } else if (host_os == "mac") {
++ host_toolchain = "//build/toolchain/mac:clang_$host_cpu"
++ } else {
++ assert(false, "Unknown host for ohos cross compile")
++ }
+ } else if (is_win) {
+ assert(is_clang)
+ host_toolchain = "//build/toolchain/win:clang_$host_cpu"
+@@ -550,6 +583,19 @@ if (custom_toolchain != "") {
+ } else {
+ set_default_toolchain("//build/toolchain/android:$current_cpu")
+ }
++} else if (is_ohos) {
++ set_default_toolchain("//build/toolchain/custom")
++ if (host_os == "linux") {
++ if (is_clang) {
++ host_toolchain = "//build/toolchain/linux:clang_$host_cpu"
++ } else {
++ host_toolchain = "//build/toolchain/linux:$host_cpu"
++ }
++ } else if (host_os == "mac") {
++ host_toolchain = "//build/toolchain/mac:clang_$host_cpu"
++ } else {
++ assert(false, "Unknown host for ohos cross compile")
++ }
+ } else if (is_linux) {
+ if (is_clang) {
+ host_toolchain = "//build/toolchain/linux:clang_$host_cpu"
+diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
+index 51d0b97..08b5f08 100644
+--- a/build/config/compiler/BUILD.gn
++++ b/build/config/compiler/BUILD.gn
+@@ -206,10 +206,16 @@ config("compiler") {
+ # CPU architecture. We may or may not be doing a cross compile now, so for
+ # simplicity we always explicitly set the architecture.
+ if (current_cpu == "x64") {
+- cflags += [
+- "-m64",
+- "-march=x86-64",
+- ]
++ if (is_ohos) {
++ cflags += [
++ "-m64",
++ ]
++ } else {
++ cflags += [
++ "-m64",
++ "-march=x86-64",
++ ]
++ }
+ ldflags += [ "-m64" ]
+ } else if (current_cpu == "x86") {
+ cflags += [ "-m32" ]
+@@ -335,7 +341,7 @@ config("compiler") {
+
+ # Linux/Android common flags setup.
+ # ---------------------------------
+- if (is_linux || is_android) {
++ if (is_linux || is_android || is_ohos) {
+ cflags += [
+ "-fPIC",
+ "-pipe", # Use pipes for communicating between sub-processes. Faster.
+@@ -354,7 +360,16 @@ config("compiler") {
+
+ # Linux-specific compiler flags setup.
+ # ------------------------------------
+- if (is_linux) {
++ if (is_ohos) {
++ cflags += [ "-pthread" ]
++ ldflags += [ "-pthread" ]
++
++ if (current_cpu == "arm64") {
++ cflags += [ "--target=aarch64-linux-ohos" ]
++ ldflags += [ "--target=aarch64-linux-ohos" ]
++ cflags += [ "-DBORINGSSL_CLANG_SUPPORTS_DOT_ARCH" ]
++ }
++ } else if (is_linux) {
+ cflags += [ "-pthread" ]
+ ldflags += [ "-pthread" ]
+
+@@ -532,9 +547,13 @@ config("runtime_library") {
+ ldflags += [ "-nostdlib++" ]
+ }
+ include_dirs = [
+- "//third_party/libcxx/include",
+ "//third_party/libcxxabi/include",
+ ]
++ if (custom_toolchain != "") {
++ include_dirs += [ "$custom_toolchain/include/c++/v1" ]
++ } else {
++ include_dirs += [ "//third_party/libcxx/include" ]
++ }
+ }
+
+ # Android standard library setup.
+@@ -661,7 +680,7 @@ if (is_win) {
+ "-Wno-psabi",
+ ]
+
+- if (!is_android) {
++ if (!is_android && !is_ohos) {
+ default_warning_flags += [
+ # Needed for nlohmann/json.
+ "-Wno-deprecated-literal-operator",
+diff --git a/build/config/ohos/BUILD.gn b/build/config/ohos/BUILD.gn
+new file mode 100644
+index 0000000..b2675d2
+--- /dev/null
++++ b/build/config/ohos/BUILD.gn
+@@ -0,0 +1,29 @@
++# Copyright (c) 2013 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++import("//build/config/ohos/config.gni")
++import("//build/config/ohos/pkg_config.gni")
++import("//build/config/features.gni")
++import("//build/config/sysroot.gni")
++import("//build/config/ui.gni")
++
++config("sdk") {
++ if (sysroot != "") {
++ cflags = [ "--sysroot=" + sysroot ]
++ ldflags = [ "--sysroot=" + sysroot ]
++
++ # Need to get some linker flags out of the sysroot.
++ ldflags += [ exec_script("sysroot_ld_path.py",
++ [
++ rebase_path("//build/ohos/sysroot_ld_path.sh",
++ root_build_dir),
++ sysroot,
++ ],
++ "value") ]
++ }
++}
++
++config("fontconfig") {
++ libs = [ ]
++}
+diff --git a/build/config/ohos/config.gni b/build/config/ohos/config.gni
+new file mode 100644
+index 0000000..0022d70
+--- /dev/null
++++ b/build/config/ohos/config.gni
+@@ -0,0 +1,13 @@
++# Copyright 2014 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++# This file contains common system config stuff for the Ohos build.
++
++if (is_ohos) {
++ if (current_cpu == "arm64") {
++ ohos_app_abi = "arm64-v8a"
++ } else {
++ assert(false, "Unknown Ohos ABI: " + current_cpu)
++ }
++}
+diff --git a/build/config/ohos/pkg-config.py b/build/config/ohos/pkg-config.py
+new file mode 100644
+index 0000000..b4a6aff
+--- /dev/null
++++ b/build/config/ohos/pkg-config.py
+@@ -0,0 +1,249 @@
++#!/usr/bin/env python3
++#
++# Copyright (c) 2013 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++
++
++import json
++import os
++import subprocess
++import sys
++import re
++from optparse import OptionParser
++
++# This script runs pkg-config, optionally filtering out some results, and
++# returns the result.
++#
++# The result will be [ , , , , ]
++# where each member is itself a list of strings.
++#
++# You can filter out matches using "-v " where all results from
++# pkgconfig matching the given regular expression will be ignored. You can
++# specify more than one regular expression my specifying "-v" more than once.
++#
++# You can specify a sysroot using "-s " where sysroot is the absolute
++# system path to the sysroot used for compiling. This script will attempt to
++# generate correct paths for the sysroot.
++#
++# When using a sysroot, you must also specify the architecture via
++# "-a " where arch is either "x86" or "x64".
++#
++# CrOS systemroots place pkgconfig files at /usr/share/pkgconfig
++# and one of /usr/lib/pkgconfig or /usr/lib64/pkgconfig
++# depending on whether the systemroot is for a 32 or 64 bit architecture. They
++# specify the 'lib' or 'lib64' of the pkgconfig path by defining the
++# 'system_libdir' variable in the args.gn file. pkg_config.gni communicates this
++# variable to this script with the "--system_libdir " flag. If no
++# flag is provided, then pkgconfig files are assumed to come from
++# /usr/lib/pkgconfig.
++#
++# Additionally, you can specify the option --atleast-version. This will skip
++# the normal outputting of a dictionary and instead print true or false,
++# depending on the return value of pkg-config for the given package.
++
++
++def SetConfigPath(options):
++ """Set the PKG_CONFIG_LIBDIR environment variable.
++
++ This takes into account any sysroot and architecture specification from the
++ options on the given command line.
++ """
++
++ sysroot = options.sysroot
++ assert sysroot
++
++ # Compute the library path name based on the architecture.
++ arch = options.arch
++ if sysroot and not arch:
++ print("You must specify an architecture via -a if using a sysroot.")
++ sys.exit(1)
++
++ libdir = sysroot + '/usr/' + options.system_libdir + '/pkgconfig'
++ libdir += ':' + sysroot + '/usr/share/pkgconfig'
++ os.environ['PKG_CONFIG_LIBDIR'] = libdir
++ return libdir
++
++
++def GetPkgConfigPrefixToStrip(options, args):
++ """Returns the prefix from pkg-config where packages are installed.
++
++ This returned prefix is the one that should be stripped from the beginning of
++ directory names to take into account sysroots.
++ """
++ # Some sysroots, like the Chromium OS ones, may generate paths that are not
++ # relative to the sysroot. For example,
++ # /path/to/chroot/build/x86-generic/usr/lib/pkgconfig/pkg.pc may have all
++ # paths relative to /path/to/chroot (i.e. prefix=/build/x86-generic/usr)
++ # instead of relative to /path/to/chroot/build/x86-generic (i.e prefix=/usr).
++ # To support this correctly, it's necessary to extract the prefix to strip
++ # from pkg-config's |prefix| variable.
++ prefix = subprocess.check_output([options.pkg_config,
++ "--variable=prefix"] + args, env=os.environ).decode('utf-8')
++ if prefix[-4] == '/usr':
++ return prefix[4:]
++ return prefix
++
++
++def MatchesAnyRegexp(flag, list_of_regexps):
++ """Returns true if the first argument matches any regular expression in the
++ given list."""
++ for regexp in list_of_regexps:
++ if regexp.search(flag) != None:
++ return True
++ return False
++
++
++def RewritePath(path, strip_prefix, sysroot):
++ """Rewrites a path by stripping the prefix and prepending the sysroot."""
++ if os.path.isabs(path) and not path.startswith(sysroot):
++ if path.startswith(strip_prefix):
++ path = path[len(strip_prefix):]
++ path = path.lstrip('/')
++ return os.path.join(sysroot, path)
++ else:
++ return path
++
++
++def main():
++ # If this is run on non-Linux platforms, just return nothing and indicate
++ # success. This allows us to "kind of emulate" a Linux build from other
++ # platforms.
++ if "linux" not in sys.platform:
++ print("[[],[],[],[],[]]")
++ return 0
++
++ parser = OptionParser()
++ parser.add_option('-d', '--debug', action='store_true')
++ parser.add_option('-p', action='store', dest='pkg_config', type='string',
++ default='pkg-config')
++ parser.add_option('-v', action='append', dest='strip_out', type='string')
++ parser.add_option('-s', action='store', dest='sysroot', type='string')
++ parser.add_option('-a', action='store', dest='arch', type='string')
++ parser.add_option('--system_libdir', action='store', dest='system_libdir',
++ type='string', default='lib')
++ parser.add_option('--atleast-version', action='store',
++ dest='atleast_version', type='string')
++ parser.add_option('--libdir', action='store_true', dest='libdir')
++ parser.add_option('--dridriverdir', action='store_true', dest='dridriverdir')
++ parser.add_option('--version-as-components', action='store_true',
++ dest='version_as_components')
++ (options, args) = parser.parse_args()
++
++ # Make a list of regular expressions to strip out.
++ strip_out = []
++ if options.strip_out != None:
++ for regexp in options.strip_out:
++ strip_out.append(re.compile(regexp))
++
++ if options.sysroot:
++ libdir = SetConfigPath(options)
++ if options.debug:
++ sys.stderr.write('PKG_CONFIG_LIBDIR=%s\n' % libdir)
++ prefix = GetPkgConfigPrefixToStrip(options, args)
++ else:
++ prefix = ''
++
++ if options.atleast_version:
++ # When asking for the return value, just run pkg-config and print the return
++ # value, no need to do other work.
++ if not subprocess.call([options.pkg_config,
++ "--atleast-version=" + options.atleast_version] +
++ args):
++ print("true")
++ else:
++ print("false")
++ return 0
++
++ if options.version_as_components:
++ cmd = [options.pkg_config, "--modversion"] + args
++ try:
++ version_string = subprocess.check_output(cmd).decode('utf-8')
++ except:
++ sys.stderr.write('Error from pkg-config.\n')
++ return 1
++ print(json.dumps(list(map(int, version_string.strip().split(".")))))
++ return 0
++
++
++ if options.libdir:
++ cmd = [options.pkg_config, "--variable=libdir"] + args
++ if options.debug:
++ sys.stderr.write('Running: %s\n' % cmd)
++ try:
++ libdir = subprocess.check_output(cmd).decode('utf-8')
++ except:
++ print("Error from pkg-config.")
++ return 1
++ sys.stdout.write(libdir.strip())
++ return 0
++
++ if options.dridriverdir:
++ cmd = [options.pkg_config, "--variable=dridriverdir"] + args
++ if options.debug:
++ sys.stderr.write('Running: %s\n' % cmd)
++ try:
++ dridriverdir = subprocess.check_output(cmd).decode('utf-8')
++ except:
++ print("Error from pkg-config.")
++ return 1
++ sys.stdout.write(dridriverdir.strip())
++ return
++
++ cmd = [options.pkg_config, "--cflags", "--libs"] + args
++ if options.debug:
++ sys.stderr.write('Running: %s\n' % ' '.join(cmd))
++
++ try:
++ flag_string = subprocess.check_output(cmd).decode('utf-8')
++ except:
++ sys.stderr.write('Could not run pkg-config.\n')
++ return 1
++
++ # For now just split on spaces to get the args out. This will break if
++ # pkgconfig returns quoted things with spaces in them, but that doesn't seem
++ # to happen in practice.
++ all_flags = flag_string.strip().split(' ')
++
++
++ sysroot = options.sysroot
++ if not sysroot:
++ sysroot = ''
++
++ includes = []
++ cflags = []
++ libs = []
++ lib_dirs = []
++
++ for flag in all_flags[:]:
++ if len(flag) == 0 or MatchesAnyRegexp(flag, strip_out):
++ continue;
++
++ if flag[:2] == '-l':
++ libs.append(RewritePath(flag[2:], prefix, sysroot))
++ elif flag[:2] == '-L':
++ lib_dirs.append(RewritePath(flag[2:], prefix, sysroot))
++ elif flag[:2] == '-I':
++ includes.append(RewritePath(flag[2:], prefix, sysroot))
++ elif flag[:3] == '-Wl':
++ # Don't allow libraries to control ld flags. These should be specified
++ # only in build files.
++ pass
++ elif flag == '-pthread':
++ # Many libs specify "-pthread" which we don't need since we always include
++ # this anyway. Removing it here prevents a bunch of duplicate inclusions
++ # on the command line.
++ pass
++ else:
++ cflags.append(flag)
++
++ # Output a GN array, the first one is the cflags, the second are the libs. The
++ # JSON formatter prints GN compatible lists when everything is a list of
++ # strings.
++ print(json.dumps([includes, cflags, libs, lib_dirs]))
++ return 0
++
++
++if __name__ == '__main__':
++ sys.exit(main())
+diff --git a/build/config/ohos/pkg_config.gni b/build/config/ohos/pkg_config.gni
+new file mode 100644
+index 0000000..f970ac1
+--- /dev/null
++++ b/build/config/ohos/pkg_config.gni
+@@ -0,0 +1,131 @@
++# Copyright (c) 2013 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++import("//build/config/sysroot.gni")
++
++# Defines a config specifying the result of running pkg-config for the given
++# packages. Put the package names you want to query in the "packages" variable
++# inside the template invocation.
++#
++# You can also add defines via the "defines" variable. This can be useful to
++# add this to the config to pass defines that the library expects to get by
++# users of its headers.
++#
++# Example:
++# pkg_config("mything") {
++# packages = [ "mything1", "mything2" ]
++# defines = [ "ENABLE_AWESOME" ]
++# }
++#
++# You can also use "extra args" to filter out results (see pkg-config.py):
++# extra_args = [ "-v, "foo" ]
++# To ignore libs and ldflags (only cflags/defines will be set, which is useful
++# when doing manual dynamic linking), set:
++# ignore_libs = true
++
++declare_args() {
++ # A pkg-config wrapper to call instead of trying to find and call the right
++ # pkg-config directly. Wrappers like this are common in cross-compilation
++ # environments.
++ # Leaving it blank defaults to searching PATH for 'pkg-config' and relying on
++ # the sysroot mechanism to find the right .pc files.
++ pkg_config = ""
++
++ # A optional pkg-config wrapper to use for tools built on the host.
++ host_pkg_config = ""
++
++ # CrOS systemroots place pkgconfig files at /usr/share/pkgconfig
++ # and one of /usr/lib/pkgconfig or /usr/lib64/pkgconfig
++ # depending on whether the systemroot is for a 32 or 64 bit architecture.
++ #
++ # When build under GYP, CrOS board builds specify the 'system_libdir' variable
++ # as part of the GYP_DEFINES provided by the CrOS emerge build or simple
++ # chrome build scheme. This variable permits controlling this for GN builds
++ # in similar fashion by setting the `system_libdir` variable in the build's
++ # args.gn file to 'lib' or 'lib64' as appropriate for the target architecture.
++ system_libdir = "lib"
++}
++
++pkg_config_script = "//build/config/ohos/pkg-config.py"
++
++# Define the args we pass to the pkg-config script for other build files that
++# need to invoke it manually.
++pkg_config_args = []
++
++if (sysroot != "") {
++ # Pass the sysroot if we're using one (it requires the CPU arch also).
++ pkg_config_args += [
++ "-s",
++ rebase_path(sysroot),
++ "-a",
++ current_cpu,
++ ]
++}
++
++if (pkg_config != "") {
++ pkg_config_args += [
++ "-p",
++ pkg_config,
++ ]
++}
++
++# Only use the custom libdir when building with the target sysroot.
++if (target_sysroot != "" && sysroot == target_sysroot) {
++ pkg_config_args += [
++ "--system_libdir",
++ system_libdir,
++ ]
++}
++
++if (host_pkg_config != "") {
++ host_pkg_config_args = [
++ "-p",
++ host_pkg_config,
++ ]
++} else {
++ host_pkg_config_args = pkg_config_args
++}
++
++template("pkg_config") {
++ assert(defined(invoker.packages),
++ "Variable |packages| must be defined to be a list in pkg_config.")
++ config(target_name) {
++ if (host_toolchain == current_toolchain) {
++ args = host_pkg_config_args + invoker.packages
++ } else {
++ args = pkg_config_args + invoker.packages
++ }
++ if (defined(invoker.extra_args)) {
++ args += invoker.extra_args
++ }
++
++ pkgresult = exec_script(pkg_config_script, args, "value")
++ cflags = pkgresult[1]
++
++ foreach(include, pkgresult[0]) {
++ if (sysroot != "") {
++ # We want the system include paths to use -isystem instead of -I to
++ # suppress warnings in those headers.
++ include_relativized = rebase_path(include, root_build_dir)
++ cflags += [ "-isystem$include_relativized" ]
++ } else {
++ cflags += [ "-I$include" ]
++ }
++ }
++
++ if (!defined(invoker.ignore_libs) || !invoker.ignore_libs) {
++ libs = pkgresult[2]
++ lib_dirs = pkgresult[3]
++ }
++
++ forward_variables_from(invoker,
++ [
++ "defines",
++ "visibility",
++ ])
++ }
++}
++
++OHOS_NDK_ROOT = target_sysroot
++OHOS_NDK_LIB = "$OHOS_NDK_ROOT/usr/aarch64-linux-ohos"
+diff --git a/build/config/ohos/sysroot_ld_path.py b/build/config/ohos/sysroot_ld_path.py
+new file mode 100644
+index 0000000..2cde6e2
+--- /dev/null
++++ b/build/config/ohos/sysroot_ld_path.py
+@@ -0,0 +1,21 @@
++# Copyright (c) 2013 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++# This file takes two arguments, the relative location of the shell script that
++# does the checking, and the name of the sysroot.
++
++# TODO(brettw) the build/linux/sysroot_ld_path.sh script should be rewritten in
++# Python in this file.
++
++import subprocess
++import sys
++
++if len(sys.argv) != 3:
++ print("Need two arguments")
++ sys.exit(1)
++
++result = subprocess.check_output([sys.argv[1], sys.argv[2]],
++ universal_newlines=True).strip()
++
++print('"%s"' % result)
+diff --git a/build/config/sysroot.gni b/build/config/sysroot.gni
+index 727a6b1..9f68166 100644
+--- a/build/config/sysroot.gni
++++ b/build/config/sysroot.gni
+@@ -22,8 +22,10 @@ if (current_toolchain == default_toolchain && target_sysroot != "") {
+ } else if (is_android) {
+ import("//build/config/android/config.gni")
+ sysroot = rebase_path("$android_toolchain_root/sysroot", root_build_dir)
++} else if (is_ohos) {
++ sysroot = target_sysroot
+ } else if (is_linux && !is_chromeos) {
+- if (use_default_linux_sysroot && !is_fuchsia) {
++ if (use_default_linux_sysroot) {
+ if (current_cpu == "x64") {
+ sysroot =
+ rebase_path("//build/linux/debian_sid_amd64-sysroot", root_build_dir)
+diff --git a/build/ohos/ohos_create_flutter_har.py b/build/ohos/ohos_create_flutter_har.py
+new file mode 100644
+index 0000000..fc46359
+--- /dev/null
++++ b/build/ohos/ohos_create_flutter_har.py
+@@ -0,0 +1,133 @@
++#!/usr/bin/env python3
++#
++# Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
++# Licensed under the Apache License, Version 2.0 (the "License");
++# you may not use this file except in compliance with the License.
++# You may obtain a copy of the License at
++#
++# http://www.apache.org/licenses/LICENSE-2.0
++#
++# Unless required by applicable law or agreed to in writing, software
++# distributed under the License is distributed on an "AS IS" BASIS,
++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++# See the License for the specific language governing permissions and
++# limitations under the License.
++
++"""Create a HAR incorporating all the components required to build a Flutter application"""
++
++import argparse
++import logging
++import os
++import re
++import shutil
++import subprocess
++import sys
++
++
++HAR_CONFIG_TEMPLATE = """
++{
++ "app": {
++ "signingConfigs": [],
++ "products": [
++ {
++ "name": "default",
++ "signingConfig": "default",
++ "compileSdkVersion": "%s",
++ "compatibleSdkVersion": "%s",
++ "runtimeOS": "HarmonyOS",
++ }
++ ],
++ "buildModeSet": [
++ {
++ "name": "debug",
++ },
++ {
++ "name": "release"
++ },
++ {
++ "name": "profile"
++ },
++ ]
++ },
++ "modules": [
++ {
++ "name": "flutter",
++ "srcPath": "./flutter"
++ }
++ ]
++}
++"""
++
++
++# 更新har的配置文件,指定编译使用的api版本
++def updateConfig(buildDir, apiInt):
++ apiStr = "4.1.0(11)" if apiInt == 11 else "5.0.0(12)"
++ jsonFile = os.path.join(buildDir, "build-profile.json5")
++ with open(jsonFile, "w", encoding="utf-8") as file:
++ file.write(HAR_CONFIG_TEMPLATE % (apiStr, apiStr))
++
++
++# 执行命令
++def runCommand(command, checkCode=True, timeout=None):
++ logging.info("runCommand start, command = %s" % (command))
++ code = subprocess.Popen(command, shell=True).wait(timeout)
++ if code != 0:
++ logging.error("runCommand error, code = %s, command = %s" % (code, command))
++ if checkCode:
++ exit(code)
++ else:
++ logging.info("runCommand finish, code = %s, command = %s" % (code, command))
++
++
++# 编译har文件,通过hvigorw的命令行参数指定编译类型(debug/release/profile)
++def buildHar(buildDir, apiInt, buildType):
++ updateConfig(buildDir, apiInt)
++ runCommand(
++ "cd %s && hvigorw clean --mode module " % (buildDir)
++ + "-p module=flutter@default -p product=default -p buildMode=%s " % buildType
++ + "assembleHar --no-daemon"
++ )
++
++
++def main():
++ parser = argparse.ArgumentParser()
++ parser.add_argument("--embedding_src", help="Path of embedding source code.")
++ parser.add_argument("--build_dir", help="Path to build.")
++ parser.add_argument(
++ "--build_type",
++ choices=["debug", "release", "profile"],
++ help="Type to build har.",
++ )
++ parser.add_argument("--output", help="Path to output har.")
++ parser.add_argument("--native_lib", action="append", help="Native code library.")
++ parser.add_argument("--ohos_abi", help="Native code ABI.")
++ parser.add_argument(
++ "--ohos_api_int", type=int, choices=[11, 12], help="Ohos api int."
++ )
++ options = parser.parse_args()
++ # copy source code
++ if os.path.exists(options.build_dir):
++ shutil.rmtree(options.build_dir)
++ shutil.copytree(options.embedding_src, options.build_dir)
++
++ # copy so files
++ for file in options.native_lib:
++ dir_name, full_file_name = os.path.split(file)
++ targetDir = os.path.join(options.build_dir, "flutter/libs", options.ohos_abi)
++ if not os.path.exists(targetDir):
++ os.makedirs(targetDir)
++ shutil.copyfile(
++ file,
++ os.path.join(targetDir, full_file_name),
++ )
++ buildHar(options.build_dir, options.ohos_api_int, options.build_type)
++ shutil.copyfile(
++ os.path.join(
++ options.build_dir, "flutter/build/default/outputs/default/flutter.har"
++ ),
++ options.output,
++ )
++
++
++if __name__ == "__main__":
++ sys.exit(main())
+diff --git a/build/ohos/sysroot_ld_path.sh b/build/ohos/sysroot_ld_path.sh
+new file mode 100755
+index 0000000..4b8bf73
+--- /dev/null
++++ b/build/ohos/sysroot_ld_path.sh
+@@ -0,0 +1,100 @@
++#!/bin/sh
++# Copyright (c) 2013 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++# Reads etc/ld.so.conf and/or etc/ld.so.conf.d/*.conf and returns the
++# appropriate linker flags.
++#
++# sysroot_ld_path.sh /abspath/to/sysroot
++#
++
++log_error_and_exit() {
++ echo $0: $@
++ exit 1
++}
++
++process_entry() {
++ if [ -z "$1" ] || [ -z "$2" ]; then
++ log_error_and_exit "bad arguments to process_entry()"
++ fi
++ local root="$1"
++ local localpath="$2"
++
++ echo $localpath | grep -qs '^/'
++ if [ $? -ne 0 ]; then
++ log_error_and_exit $localpath does not start with /
++ fi
++ local entry="$root$localpath"
++ echo -L$entry
++ echo -Wl,-rpath-link=$entry
++}
++
++process_ld_so_conf() {
++ if [ -z "$1" ] || [ -z "$2" ]; then
++ log_error_and_exit "bad arguments to process_ld_so_conf()"
++ fi
++ local root="$1"
++ local ld_so_conf="$2"
++
++ # ld.so.conf may include relative include paths. pushd is a bashism.
++ local saved_pwd=$(pwd)
++ cd $(dirname "$ld_so_conf")
++
++ cat "$ld_so_conf" | \
++ while read ENTRY; do
++ echo "$ENTRY" | grep -qs ^include
++ if [ $? -eq 0 ]; then
++ local included_files=$(echo "$ENTRY" | sed 's/^include //')
++ echo "$included_files" | grep -qs ^/
++ if [ $? -eq 0 ]; then
++ if ls $root$included_files >/dev/null 2>&1 ; then
++ for inc_file in $root$included_files; do
++ process_ld_so_conf "$root" "$inc_file"
++ done
++ fi
++ else
++ if ls $(pwd)/$included_files >/dev/null 2>&1 ; then
++ for inc_file in $(pwd)/$included_files; do
++ process_ld_so_conf "$root" "$inc_file"
++ done
++ fi
++ fi
++ continue
++ fi
++
++ echo "$ENTRY" | grep -qs ^/
++ if [ $? -eq 0 ]; then
++ process_entry "$root" "$ENTRY"
++ fi
++ done
++
++ # popd is a bashism
++ cd "$saved_pwd"
++}
++
++# Main
++
++if [ $# -ne 1 ]; then
++ echo Usage $0 /abspath/to/sysroot
++ exit 1
++fi
++
++echo $1 | grep -qs ' '
++if [ $? -eq 0 ]; then
++ log_error_and_exit $1 contains whitespace.
++fi
++
++LD_SO_CONF="$1/etc/ld.so.conf"
++LD_SO_CONF_D="$1/etc/ld.so.conf.d"
++
++if [ -e "$LD_SO_CONF" ]; then
++ process_ld_so_conf "$1" "$LD_SO_CONF" | xargs echo
++elif [ -e "$LD_SO_CONF_D" ]; then
++ find "$LD_SO_CONF_D" -maxdepth 1 -name '*.conf' -print -quit > /dev/null
++ if [ $? -eq 0 ]; then
++ for entry in $LD_SO_CONF_D/*.conf; do
++ process_ld_so_conf "$1" "$entry"
++ done | xargs echo
++ fi
++fi
+diff --git a/build/toolchain/custom/BUILD.gn b/build/toolchain/custom/BUILD.gn
+index 3da5f93..05de4d7 100644
+--- a/build/toolchain/custom/BUILD.gn
++++ b/build/toolchain/custom/BUILD.gn
+@@ -12,11 +12,11 @@ toolchain("custom") {
+ # these values in our scope.
+ cc = "${toolchain_bin}/clang"
+ cxx = "${toolchain_bin}/clang++"
+- ar = "${toolchain_bin}/${custom_target_triple}-ar"
++ ar = "${toolchain_bin}/llvm-ar"
+ ld = "${toolchain_bin}/clang++"
+- readelf = "${toolchain_bin}/${custom_target_triple}-readelf"
+- nm = "${toolchain_bin}/${custom_target_triple}-nm"
+- strip = "${toolchain_bin}/${custom_target_triple}-strip"
++ readelf = "${toolchain_bin}/llvm-readelf"
++ nm = "${toolchain_bin}/llvm-nm"
++ strip = "${toolchain_bin}/llvm-strip"
+
+ target_triple_flags = "--target=${custom_target_triple}"
+ sysroot_flags = "--sysroot ${custom_sysroot}"
diff --git a/attachment/repos/build-3.22.patch2 b/attachment/repos/build-3.22.patch2
new file mode 100644
index 0000000000000000000000000000000000000000..52468e1a96b965abeac3032431a1c3314e30b836
--- /dev/null
+++ b/attachment/repos/build-3.22.patch2
@@ -0,0 +1,19 @@
+diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
+index 08b5f08..ac21f66 100644
+--- a/build/config/compiler/BUILD.gn
++++ b/build/config/compiler/BUILD.gn
+@@ -548,12 +548,8 @@ config("runtime_library") {
+ }
+ include_dirs = [
++ "//third_party/libcxx/include",
+ "//third_party/libcxxabi/include",
+ ]
+- if (custom_toolchain != "") {
+- include_dirs += [ "$custom_toolchain/include/c++/v1" ]
+- } else {
+- include_dirs += [ "//third_party/libcxx/include" ]
+- }
+ }
+
+ # Android standard library setup.
+--
\ No newline at end of file
diff --git a/attachment/repos/build.patch b/attachment/repos/build.patch
new file mode 100644
index 0000000000000000000000000000000000000000..bbe17f9809dd6752ee533c0b15c1ec6c84574e57
--- /dev/null
+++ b/attachment/repos/build.patch
@@ -0,0 +1,919 @@
+diff --git a/build/config/BUILD.gn b/build/config/BUILD.gn
+index 4e62965..ea49a53 100644
+--- a/build/config/BUILD.gn
++++ b/build/config/BUILD.gn
+@@ -146,6 +146,8 @@ config("default_libs") {
+ "dl",
+ "m",
+ ]
++ } else if (is_ohos) {
++ libs = [ "dl","hilog_ndk.z" ]
+ } else if (is_linux) {
+ libs = [ "dl" ]
+ }
+diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn
+index b8d0f47..a3d1ed9 100644
+--- a/build/config/BUILDCONFIG.gn
++++ b/build/config/BUILDCONFIG.gn
+@@ -190,6 +190,7 @@ if (current_os == "win") {
+ is_fuchsia_host = false
+ is_ios = false
+ is_linux = false
++ is_ohos = false
+ is_mac = false
+ is_posix = false
+ is_win = true
+@@ -198,6 +199,7 @@ if (current_os == "win") {
+ is_android = false
+ is_chromeos = false
+ is_fuchsia = false
++ is_ohos = false
+ is_fuchsia_host = false
+ is_ios = false
+ is_linux = false
+@@ -212,6 +214,7 @@ if (current_os == "win") {
+ is_fuchsia_host = false
+ is_ios = false
+ is_linux = false
++ is_ohos = false
+ is_mac = false
+ is_posix = true
+ is_win = false
+@@ -223,6 +226,7 @@ if (current_os == "win") {
+ is_fuchsia_host = false
+ is_ios = false
+ is_linux = true
++ is_ohos = false
+ is_mac = false
+ is_posix = true
+ is_win = false
+@@ -237,6 +241,7 @@ if (current_os == "win") {
+ is_fuchsia_host = false
+ is_ios = false
+ is_linux = false
++ is_ohos = false
+ is_mac = false
+ is_posix = true
+ is_win = false
+@@ -248,6 +253,19 @@ if (current_os == "win") {
+ is_fuchsia_host = false
+ is_ios = true
+ is_linux = false
++ is_ohos = false
++ is_mac = false
++ is_posix = true
++ is_win = false
++ is_wasm = false
++} else if (current_os == "ohos") {
++ is_android = false
++ is_chromeos = false
++ is_fuchsia = false
++ is_fuchsia_host = false
++ is_ios = false
++ is_linux = false
++ is_ohos = true
+ is_mac = false
+ is_posix = true
+ is_win = false
+@@ -259,6 +277,7 @@ if (current_os == "win") {
+ is_fuchsia_host = false
+ is_ios = false
+ is_linux = true
++ is_ohos = false
+ is_mac = false
+ is_posix = true
+ is_win = false
+@@ -270,6 +289,7 @@ if (current_os == "win") {
+ is_fuchsia_host = false
+ is_ios = false
+ is_linux = false
++ is_ohos = false
+ is_mac = false
+ is_posix = true
+ is_win = false
+@@ -281,6 +301,7 @@ if (current_os == "win") {
+ is_fuchsia_host = false
+ is_ios = false
+ is_linux = false
++ is_ohos = false
+ is_mac = false
+ is_posix = false
+ is_win = false
+@@ -313,7 +334,7 @@ if (!is_clang && using_sanitizer) {
+ is_clang = true
+ }
+
+-use_flutter_cxx = is_clang && (is_linux || is_android || is_mac || is_ios)
++use_flutter_cxx = is_clang && (is_linux || is_android || is_mac || is_ios || is_ohos )
+
+ if (is_msan && !is_linux) {
+ assert(false, "Memory sanitizer is only available on Linux.")
+@@ -365,7 +386,9 @@ if (is_posix) {
+ ]
+ }
+
+-if (is_linux) {
++if (is_ohos || (is_linux && host_os == "mac")) {
++ _native_compiler_configs += [ "//build/config/ohos:sdk" ]
++} else if (is_linux) {
+ _native_compiler_configs += [ "//build/config/linux:sdk" ]
+ } else if (is_mac) {
+ _native_compiler_configs += [ "//build/config/mac:sdk" ]
+@@ -510,8 +533,18 @@ shlib_toolchain = false
+ if (custom_toolchain != "") {
+ assert(custom_sysroot != "")
+ assert(custom_target_triple != "")
+- host_toolchain = "//build/toolchain/linux:clang_$host_cpu"
+ set_default_toolchain("//build/toolchain/custom")
++ if (host_os == "linux") {
++ if (is_clang) {
++ host_toolchain = "//build/toolchain/linux:clang_$host_cpu"
++ } else {
++ host_toolchain = "//build/toolchain/linux:$host_cpu"
++ }
++ } else if (host_os == "mac") {
++ host_toolchain = "//build/toolchain/mac:clang_$host_cpu"
++ } else {
++ assert(false, "Unknown host for ohos cross compile")
++ }
+ } else if (is_win) {
+ if (is_clang) {
+ host_toolchain = "//build/toolchain/win:clang_$host_cpu"
+@@ -544,6 +577,19 @@ if (custom_toolchain != "") {
+ } else {
+ set_default_toolchain("//build/toolchain/android:$current_cpu")
+ }
++} else if (is_ohos) {
++ set_default_toolchain("//build/toolchain/custom")
++ if (host_os == "linux") {
++ if (is_clang) {
++ host_toolchain = "//build/toolchain/linux:clang_$host_cpu"
++ } else {
++ host_toolchain = "//build/toolchain/linux:$host_cpu"
++ }
++ } else if (host_os == "mac") {
++ host_toolchain = "//build/toolchain/mac:clang_$host_cpu"
++ } else {
++ assert(false, "Unknown host for ohos cross compile")
++ }
+ } else if (is_linux) {
+ if (is_clang) {
+ host_toolchain = "//build/toolchain/linux:clang_$host_cpu"
+diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
+index ba20010..17e0487 100644
+--- a/build/config/compiler/BUILD.gn
++++ b/build/config/compiler/BUILD.gn
+@@ -191,10 +191,16 @@ config("compiler") {
+ # CPU architecture. We may or may not be doing a cross compile now, so for
+ # simplicity we always explicitly set the architecture.
+ if (current_cpu == "x64") {
+- cflags += [
+- "-m64",
+- "-march=x86-64",
+- ]
++ if (is_ohos) {
++ cflags += [
++ "-m64",
++ ]
++ } else {
++ cflags += [
++ "-m64",
++ "-march=x86-64",
++ ]
++ }
+ ldflags += [ "-m64" ]
+ } else if (current_cpu == "x86") {
+ cflags += [ "-m32" ]
+@@ -315,7 +321,7 @@ config("compiler") {
+
+ # Linux/Android common flags setup.
+ # ---------------------------------
+- if (is_linux || is_android) {
++ if (is_linux || is_android || is_ohos) {
+ cflags += [
+ "-fPIC",
+ "-pipe", # Use pipes for communicating between sub-processes. Faster.
+@@ -334,7 +340,16 @@ config("compiler") {
+
+ # Linux-specific compiler flags setup.
+ # ------------------------------------
+- if (is_linux) {
++ if (is_ohos) {
++ cflags += [ "-pthread" ]
++ ldflags += [ "-pthread" ]
++
++ if (current_cpu == "arm64") {
++ cflags += [ "--target=aarch64-linux-ohos" ]
++ ldflags += [ "--target=aarch64-linux-ohos" ]
++ cflags += [ "-DBORINGSSL_CLANG_SUPPORTS_DOT_ARCH" ]
++ }
++ } else if (is_linux) {
+ cflags += [ "-pthread" ]
+ ldflags += [ "-pthread" ]
+
+@@ -521,9 +536,13 @@ config("runtime_library") {
+ ldflags += [ "-nostdlib++" ]
+ }
+ include_dirs = [
+- "//third_party/libcxx/include",
+ "//third_party/libcxxabi/include",
+ ]
++ if (custom_toolchain != "") {
++ include_dirs += [ "$custom_toolchain/include/c++/v1" ]
++ } else {
++ include_dirs += [ "//third_party/libcxx/include" ]
++ }
+ }
+
+ # Android standard library setup.
+@@ -860,17 +879,35 @@ config("optimize") {
+ cflags = [ "-Oz" ] + common_optimize_on_cflags # Favor size over speed.
+ } else if (is_wasm) {
+ cflags = [ "-Oz" ]
++ } else if (is_ohos) {
++ cflags = [ "-O3" ] + common_optimize_on_cflags
+ } else {
+ cflags = [ "-O2" ] + common_optimize_on_cflags
+ }
+
+ lto_flags = []
+- if (enable_lto && (is_ios || is_android || is_fuchsia || is_wasm)) {
++ if (enable_lto && (is_ios || is_android || is_fuchsia || is_wasm || is_ohos)) {
+ lto_flags += [ "-flto" ]
+ }
+
++ if (is_ohos) {
++ cflags += [
++ "-mcpu=tsv110",
++ "-fwhole-program-vtables",
++ ]
++ }
++
+ ldflags = common_optimize_on_ldflags + lto_flags
+ cflags += lto_flags
++
++ if (is_ohos) {
++ ldflags += [
++ "-Wl,--lto-O2",
++ "-Wl,--plugin-opt=-mcpu=tsv110",
++ "-Wl,-mllvm",
++ "-Wl,-wholeprogramdevirt-check=fallback",
++ ]
++ }
+ }
+
+ # Turn off optimizations.
+diff --git a/build/config/ohos/BUILD.gn b/build/config/ohos/BUILD.gn
+new file mode 100644
+index 0000000..b2675d2
+--- /dev/null
++++ b/build/config/ohos/BUILD.gn
+@@ -0,0 +1,29 @@
++# Copyright (c) 2013 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++import("//build/config/ohos/config.gni")
++import("//build/config/ohos/pkg_config.gni")
++import("//build/config/features.gni")
++import("//build/config/sysroot.gni")
++import("//build/config/ui.gni")
++
++config("sdk") {
++ if (sysroot != "") {
++ cflags = [ "--sysroot=" + sysroot ]
++ ldflags = [ "--sysroot=" + sysroot ]
++
++ # Need to get some linker flags out of the sysroot.
++ ldflags += [ exec_script("sysroot_ld_path.py",
++ [
++ rebase_path("//build/ohos/sysroot_ld_path.sh",
++ root_build_dir),
++ sysroot,
++ ],
++ "value") ]
++ }
++}
++
++config("fontconfig") {
++ libs = [ ]
++}
+diff --git a/build/config/ohos/config.gni b/build/config/ohos/config.gni
+new file mode 100644
+index 0000000..0022d70
+--- /dev/null
++++ b/build/config/ohos/config.gni
+@@ -0,0 +1,13 @@
++# Copyright 2014 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++# This file contains common system config stuff for the Ohos build.
++
++if (is_ohos) {
++ if (current_cpu == "arm64") {
++ ohos_app_abi = "arm64-v8a"
++ } else {
++ assert(false, "Unknown Ohos ABI: " + current_cpu)
++ }
++}
+diff --git a/build/config/ohos/pkg-config.py b/build/config/ohos/pkg-config.py
+new file mode 100644
+index 0000000..b4a6aff
+--- /dev/null
++++ b/build/config/ohos/pkg-config.py
+@@ -0,0 +1,249 @@
++#!/usr/bin/env python3
++#
++# Copyright (c) 2013 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++
++
++import json
++import os
++import subprocess
++import sys
++import re
++from optparse import OptionParser
++
++# This script runs pkg-config, optionally filtering out some results, and
++# returns the result.
++#
++# The result will be [ , , , , ]
++# where each member is itself a list of strings.
++#
++# You can filter out matches using "-v " where all results from
++# pkgconfig matching the given regular expression will be ignored. You can
++# specify more than one regular expression my specifying "-v" more than once.
++#
++# You can specify a sysroot using "-s " where sysroot is the absolute
++# system path to the sysroot used for compiling. This script will attempt to
++# generate correct paths for the sysroot.
++#
++# When using a sysroot, you must also specify the architecture via
++# "-a " where arch is either "x86" or "x64".
++#
++# CrOS systemroots place pkgconfig files at /usr/share/pkgconfig
++# and one of /usr/lib/pkgconfig or /usr/lib64/pkgconfig
++# depending on whether the systemroot is for a 32 or 64 bit architecture. They
++# specify the 'lib' or 'lib64' of the pkgconfig path by defining the
++# 'system_libdir' variable in the args.gn file. pkg_config.gni communicates this
++# variable to this script with the "--system_libdir " flag. If no
++# flag is provided, then pkgconfig files are assumed to come from
++# /usr/lib/pkgconfig.
++#
++# Additionally, you can specify the option --atleast-version. This will skip
++# the normal outputting of a dictionary and instead print true or false,
++# depending on the return value of pkg-config for the given package.
++
++
++def SetConfigPath(options):
++ """Set the PKG_CONFIG_LIBDIR environment variable.
++
++ This takes into account any sysroot and architecture specification from the
++ options on the given command line.
++ """
++
++ sysroot = options.sysroot
++ assert sysroot
++
++ # Compute the library path name based on the architecture.
++ arch = options.arch
++ if sysroot and not arch:
++ print("You must specify an architecture via -a if using a sysroot.")
++ sys.exit(1)
++
++ libdir = sysroot + '/usr/' + options.system_libdir + '/pkgconfig'
++ libdir += ':' + sysroot + '/usr/share/pkgconfig'
++ os.environ['PKG_CONFIG_LIBDIR'] = libdir
++ return libdir
++
++
++def GetPkgConfigPrefixToStrip(options, args):
++ """Returns the prefix from pkg-config where packages are installed.
++
++ This returned prefix is the one that should be stripped from the beginning of
++ directory names to take into account sysroots.
++ """
++ # Some sysroots, like the Chromium OS ones, may generate paths that are not
++ # relative to the sysroot. For example,
++ # /path/to/chroot/build/x86-generic/usr/lib/pkgconfig/pkg.pc may have all
++ # paths relative to /path/to/chroot (i.e. prefix=/build/x86-generic/usr)
++ # instead of relative to /path/to/chroot/build/x86-generic (i.e prefix=/usr).
++ # To support this correctly, it's necessary to extract the prefix to strip
++ # from pkg-config's |prefix| variable.
++ prefix = subprocess.check_output([options.pkg_config,
++ "--variable=prefix"] + args, env=os.environ).decode('utf-8')
++ if prefix[-4] == '/usr':
++ return prefix[4:]
++ return prefix
++
++
++def MatchesAnyRegexp(flag, list_of_regexps):
++ """Returns true if the first argument matches any regular expression in the
++ given list."""
++ for regexp in list_of_regexps:
++ if regexp.search(flag) != None:
++ return True
++ return False
++
++
++def RewritePath(path, strip_prefix, sysroot):
++ """Rewrites a path by stripping the prefix and prepending the sysroot."""
++ if os.path.isabs(path) and not path.startswith(sysroot):
++ if path.startswith(strip_prefix):
++ path = path[len(strip_prefix):]
++ path = path.lstrip('/')
++ return os.path.join(sysroot, path)
++ else:
++ return path
++
++
++def main():
++ # If this is run on non-Linux platforms, just return nothing and indicate
++ # success. This allows us to "kind of emulate" a Linux build from other
++ # platforms.
++ if "linux" not in sys.platform:
++ print("[[],[],[],[],[]]")
++ return 0
++
++ parser = OptionParser()
++ parser.add_option('-d', '--debug', action='store_true')
++ parser.add_option('-p', action='store', dest='pkg_config', type='string',
++ default='pkg-config')
++ parser.add_option('-v', action='append', dest='strip_out', type='string')
++ parser.add_option('-s', action='store', dest='sysroot', type='string')
++ parser.add_option('-a', action='store', dest='arch', type='string')
++ parser.add_option('--system_libdir', action='store', dest='system_libdir',
++ type='string', default='lib')
++ parser.add_option('--atleast-version', action='store',
++ dest='atleast_version', type='string')
++ parser.add_option('--libdir', action='store_true', dest='libdir')
++ parser.add_option('--dridriverdir', action='store_true', dest='dridriverdir')
++ parser.add_option('--version-as-components', action='store_true',
++ dest='version_as_components')
++ (options, args) = parser.parse_args()
++
++ # Make a list of regular expressions to strip out.
++ strip_out = []
++ if options.strip_out != None:
++ for regexp in options.strip_out:
++ strip_out.append(re.compile(regexp))
++
++ if options.sysroot:
++ libdir = SetConfigPath(options)
++ if options.debug:
++ sys.stderr.write('PKG_CONFIG_LIBDIR=%s\n' % libdir)
++ prefix = GetPkgConfigPrefixToStrip(options, args)
++ else:
++ prefix = ''
++
++ if options.atleast_version:
++ # When asking for the return value, just run pkg-config and print the return
++ # value, no need to do other work.
++ if not subprocess.call([options.pkg_config,
++ "--atleast-version=" + options.atleast_version] +
++ args):
++ print("true")
++ else:
++ print("false")
++ return 0
++
++ if options.version_as_components:
++ cmd = [options.pkg_config, "--modversion"] + args
++ try:
++ version_string = subprocess.check_output(cmd).decode('utf-8')
++ except:
++ sys.stderr.write('Error from pkg-config.\n')
++ return 1
++ print(json.dumps(list(map(int, version_string.strip().split(".")))))
++ return 0
++
++
++ if options.libdir:
++ cmd = [options.pkg_config, "--variable=libdir"] + args
++ if options.debug:
++ sys.stderr.write('Running: %s\n' % cmd)
++ try:
++ libdir = subprocess.check_output(cmd).decode('utf-8')
++ except:
++ print("Error from pkg-config.")
++ return 1
++ sys.stdout.write(libdir.strip())
++ return 0
++
++ if options.dridriverdir:
++ cmd = [options.pkg_config, "--variable=dridriverdir"] + args
++ if options.debug:
++ sys.stderr.write('Running: %s\n' % cmd)
++ try:
++ dridriverdir = subprocess.check_output(cmd).decode('utf-8')
++ except:
++ print("Error from pkg-config.")
++ return 1
++ sys.stdout.write(dridriverdir.strip())
++ return
++
++ cmd = [options.pkg_config, "--cflags", "--libs"] + args
++ if options.debug:
++ sys.stderr.write('Running: %s\n' % ' '.join(cmd))
++
++ try:
++ flag_string = subprocess.check_output(cmd).decode('utf-8')
++ except:
++ sys.stderr.write('Could not run pkg-config.\n')
++ return 1
++
++ # For now just split on spaces to get the args out. This will break if
++ # pkgconfig returns quoted things with spaces in them, but that doesn't seem
++ # to happen in practice.
++ all_flags = flag_string.strip().split(' ')
++
++
++ sysroot = options.sysroot
++ if not sysroot:
++ sysroot = ''
++
++ includes = []
++ cflags = []
++ libs = []
++ lib_dirs = []
++
++ for flag in all_flags[:]:
++ if len(flag) == 0 or MatchesAnyRegexp(flag, strip_out):
++ continue;
++
++ if flag[:2] == '-l':
++ libs.append(RewritePath(flag[2:], prefix, sysroot))
++ elif flag[:2] == '-L':
++ lib_dirs.append(RewritePath(flag[2:], prefix, sysroot))
++ elif flag[:2] == '-I':
++ includes.append(RewritePath(flag[2:], prefix, sysroot))
++ elif flag[:3] == '-Wl':
++ # Don't allow libraries to control ld flags. These should be specified
++ # only in build files.
++ pass
++ elif flag == '-pthread':
++ # Many libs specify "-pthread" which we don't need since we always include
++ # this anyway. Removing it here prevents a bunch of duplicate inclusions
++ # on the command line.
++ pass
++ else:
++ cflags.append(flag)
++
++ # Output a GN array, the first one is the cflags, the second are the libs. The
++ # JSON formatter prints GN compatible lists when everything is a list of
++ # strings.
++ print(json.dumps([includes, cflags, libs, lib_dirs]))
++ return 0
++
++
++if __name__ == '__main__':
++ sys.exit(main())
+diff --git a/build/config/ohos/pkg_config.gni b/build/config/ohos/pkg_config.gni
+new file mode 100644
+index 0000000..f970ac1
+--- /dev/null
++++ b/build/config/ohos/pkg_config.gni
+@@ -0,0 +1,131 @@
++# Copyright (c) 2013 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++import("//build/config/sysroot.gni")
++
++# Defines a config specifying the result of running pkg-config for the given
++# packages. Put the package names you want to query in the "packages" variable
++# inside the template invocation.
++#
++# You can also add defines via the "defines" variable. This can be useful to
++# add this to the config to pass defines that the library expects to get by
++# users of its headers.
++#
++# Example:
++# pkg_config("mything") {
++# packages = [ "mything1", "mything2" ]
++# defines = [ "ENABLE_AWESOME" ]
++# }
++#
++# You can also use "extra args" to filter out results (see pkg-config.py):
++# extra_args = [ "-v, "foo" ]
++# To ignore libs and ldflags (only cflags/defines will be set, which is useful
++# when doing manual dynamic linking), set:
++# ignore_libs = true
++
++declare_args() {
++ # A pkg-config wrapper to call instead of trying to find and call the right
++ # pkg-config directly. Wrappers like this are common in cross-compilation
++ # environments.
++ # Leaving it blank defaults to searching PATH for 'pkg-config' and relying on
++ # the sysroot mechanism to find the right .pc files.
++ pkg_config = ""
++
++ # A optional pkg-config wrapper to use for tools built on the host.
++ host_pkg_config = ""
++
++ # CrOS systemroots place pkgconfig files at /usr/share/pkgconfig
++ # and one of /usr/lib/pkgconfig or /usr/lib64/pkgconfig
++ # depending on whether the systemroot is for a 32 or 64 bit architecture.
++ #
++ # When build under GYP, CrOS board builds specify the 'system_libdir' variable
++ # as part of the GYP_DEFINES provided by the CrOS emerge build or simple
++ # chrome build scheme. This variable permits controlling this for GN builds
++ # in similar fashion by setting the `system_libdir` variable in the build's
++ # args.gn file to 'lib' or 'lib64' as appropriate for the target architecture.
++ system_libdir = "lib"
++}
++
++pkg_config_script = "//build/config/ohos/pkg-config.py"
++
++# Define the args we pass to the pkg-config script for other build files that
++# need to invoke it manually.
++pkg_config_args = []
++
++if (sysroot != "") {
++ # Pass the sysroot if we're using one (it requires the CPU arch also).
++ pkg_config_args += [
++ "-s",
++ rebase_path(sysroot),
++ "-a",
++ current_cpu,
++ ]
++}
++
++if (pkg_config != "") {
++ pkg_config_args += [
++ "-p",
++ pkg_config,
++ ]
++}
++
++# Only use the custom libdir when building with the target sysroot.
++if (target_sysroot != "" && sysroot == target_sysroot) {
++ pkg_config_args += [
++ "--system_libdir",
++ system_libdir,
++ ]
++}
++
++if (host_pkg_config != "") {
++ host_pkg_config_args = [
++ "-p",
++ host_pkg_config,
++ ]
++} else {
++ host_pkg_config_args = pkg_config_args
++}
++
++template("pkg_config") {
++ assert(defined(invoker.packages),
++ "Variable |packages| must be defined to be a list in pkg_config.")
++ config(target_name) {
++ if (host_toolchain == current_toolchain) {
++ args = host_pkg_config_args + invoker.packages
++ } else {
++ args = pkg_config_args + invoker.packages
++ }
++ if (defined(invoker.extra_args)) {
++ args += invoker.extra_args
++ }
++
++ pkgresult = exec_script(pkg_config_script, args, "value")
++ cflags = pkgresult[1]
++
++ foreach(include, pkgresult[0]) {
++ if (sysroot != "") {
++ # We want the system include paths to use -isystem instead of -I to
++ # suppress warnings in those headers.
++ include_relativized = rebase_path(include, root_build_dir)
++ cflags += [ "-isystem$include_relativized" ]
++ } else {
++ cflags += [ "-I$include" ]
++ }
++ }
++
++ if (!defined(invoker.ignore_libs) || !invoker.ignore_libs) {
++ libs = pkgresult[2]
++ lib_dirs = pkgresult[3]
++ }
++
++ forward_variables_from(invoker,
++ [
++ "defines",
++ "visibility",
++ ])
++ }
++}
++
++OHOS_NDK_ROOT = target_sysroot
++OHOS_NDK_LIB = "$OHOS_NDK_ROOT/usr/aarch64-linux-ohos"
+diff --git a/build/config/ohos/sysroot_ld_path.py b/build/config/ohos/sysroot_ld_path.py
+new file mode 100644
+index 0000000..2cde6e2
+--- /dev/null
++++ b/build/config/ohos/sysroot_ld_path.py
+@@ -0,0 +1,21 @@
++# Copyright (c) 2013 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++# This file takes two arguments, the relative location of the shell script that
++# does the checking, and the name of the sysroot.
++
++# TODO(brettw) the build/linux/sysroot_ld_path.sh script should be rewritten in
++# Python in this file.
++
++import subprocess
++import sys
++
++if len(sys.argv) != 3:
++ print("Need two arguments")
++ sys.exit(1)
++
++result = subprocess.check_output([sys.argv[1], sys.argv[2]],
++ universal_newlines=True).strip()
++
++print('"%s"' % result)
+diff --git a/build/config/sysroot.gni b/build/config/sysroot.gni
+index 7987e51..cd7b060 100644
+--- a/build/config/sysroot.gni
++++ b/build/config/sysroot.gni
+@@ -22,8 +22,10 @@ if (current_toolchain == default_toolchain && target_sysroot != "") {
+ } else if (is_android) {
+ import("//build/config/android/config.gni")
+ sysroot = rebase_path("$llvm_android_toolchain_root/sysroot")
++} else if (is_ohos) {
++ sysroot = target_sysroot
+ } else if (is_linux && !is_chromeos) {
+- if (use_default_linux_sysroot && !is_fuchsia) {
++ if (use_default_linux_sysroot) {
+ if (current_cpu == "x64") {
+ sysroot = rebase_path("//build/linux/debian_sid_amd64-sysroot")
+ } else {
+diff --git a/build/ohos/sysroot_ld_path.sh b/build/ohos/sysroot_ld_path.sh
+new file mode 100755
+index 0000000..4b8bf73
+--- /dev/null
++++ b/build/ohos/sysroot_ld_path.sh
+@@ -0,0 +1,100 @@
++#!/bin/sh
++# Copyright (c) 2013 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++# Reads etc/ld.so.conf and/or etc/ld.so.conf.d/*.conf and returns the
++# appropriate linker flags.
++#
++# sysroot_ld_path.sh /abspath/to/sysroot
++#
++
++log_error_and_exit() {
++ echo $0: $@
++ exit 1
++}
++
++process_entry() {
++ if [ -z "$1" ] || [ -z "$2" ]; then
++ log_error_and_exit "bad arguments to process_entry()"
++ fi
++ local root="$1"
++ local localpath="$2"
++
++ echo $localpath | grep -qs '^/'
++ if [ $? -ne 0 ]; then
++ log_error_and_exit $localpath does not start with /
++ fi
++ local entry="$root$localpath"
++ echo -L$entry
++ echo -Wl,-rpath-link=$entry
++}
++
++process_ld_so_conf() {
++ if [ -z "$1" ] || [ -z "$2" ]; then
++ log_error_and_exit "bad arguments to process_ld_so_conf()"
++ fi
++ local root="$1"
++ local ld_so_conf="$2"
++
++ # ld.so.conf may include relative include paths. pushd is a bashism.
++ local saved_pwd=$(pwd)
++ cd $(dirname "$ld_so_conf")
++
++ cat "$ld_so_conf" | \
++ while read ENTRY; do
++ echo "$ENTRY" | grep -qs ^include
++ if [ $? -eq 0 ]; then
++ local included_files=$(echo "$ENTRY" | sed 's/^include //')
++ echo "$included_files" | grep -qs ^/
++ if [ $? -eq 0 ]; then
++ if ls $root$included_files >/dev/null 2>&1 ; then
++ for inc_file in $root$included_files; do
++ process_ld_so_conf "$root" "$inc_file"
++ done
++ fi
++ else
++ if ls $(pwd)/$included_files >/dev/null 2>&1 ; then
++ for inc_file in $(pwd)/$included_files; do
++ process_ld_so_conf "$root" "$inc_file"
++ done
++ fi
++ fi
++ continue
++ fi
++
++ echo "$ENTRY" | grep -qs ^/
++ if [ $? -eq 0 ]; then
++ process_entry "$root" "$ENTRY"
++ fi
++ done
++
++ # popd is a bashism
++ cd "$saved_pwd"
++}
++
++# Main
++
++if [ $# -ne 1 ]; then
++ echo Usage $0 /abspath/to/sysroot
++ exit 1
++fi
++
++echo $1 | grep -qs ' '
++if [ $? -eq 0 ]; then
++ log_error_and_exit $1 contains whitespace.
++fi
++
++LD_SO_CONF="$1/etc/ld.so.conf"
++LD_SO_CONF_D="$1/etc/ld.so.conf.d"
++
++if [ -e "$LD_SO_CONF" ]; then
++ process_ld_so_conf "$1" "$LD_SO_CONF" | xargs echo
++elif [ -e "$LD_SO_CONF_D" ]; then
++ find "$LD_SO_CONF_D" -maxdepth 1 -name '*.conf' -print -quit > /dev/null
++ if [ $? -eq 0 ]; then
++ for entry in $LD_SO_CONF_D/*.conf; do
++ process_ld_so_conf "$1" "$entry"
++ done | xargs echo
++ fi
++fi
+diff --git a/build/secondary/third_party/glfw/BUILD.gn b/build/secondary/third_party/glfw/BUILD.gn
+index cad6cbc..c17950b 100644
+--- a/build/secondary/third_party/glfw/BUILD.gn
++++ b/build/secondary/third_party/glfw/BUILD.gn
+@@ -47,6 +47,34 @@ source_set("glfw") {
+ ]
+
+ defines = [ "_GLFW_WIN32" ]
++ } else if (is_ohos) {
++ sources += [
++ "$_checkout_dir/src/glx_context.c",
++ "$_checkout_dir/src/glx_context.h",
++ "$_checkout_dir/src/posix_time.c",
++ "$_checkout_dir/src/posix_time.h",
++ "$_checkout_dir/src/posix_thread.c",
++ "$_checkout_dir/src/posix_thread.h",
++ "$_checkout_dir/src/x11_init.c",
++ "$_checkout_dir/src/x11_monitor.c",
++ "$_checkout_dir/src/x11_platform.h",
++ "$_checkout_dir/src/x11_window.c",
++ "$_checkout_dir/src/xkb_unicode.c",
++ "$_checkout_dir/src/xkb_unicode.h",
++ ]
++
++ defines = [
++ "_GLFW_X11",
++ "_GLFW_HAS_XF86VM",
++ ]
++
++ libs = [
++ "X11",
++ "Xcursor",
++ "Xinerama",
++ "Xrandr",
++ "Xxf86vm",
++ ]
+ } else if (is_linux) {
+ sources += [
+ "$_checkout_dir/src/glx_context.c",
+diff --git a/build/toolchain/custom/BUILD.gn b/build/toolchain/custom/BUILD.gn
+index 65b1623..a89742a 100644
+--- a/build/toolchain/custom/BUILD.gn
++++ b/build/toolchain/custom/BUILD.gn
+@@ -12,11 +12,11 @@ toolchain("custom") {
+ # these values in our scope.
+ cc = "${toolchain_bin}/clang"
+ cxx = "${toolchain_bin}/clang++"
+- ar = "${toolchain_bin}/${custom_target_triple}-ar"
++ ar = "${toolchain_bin}/llvm-ar"
+ ld = "${toolchain_bin}/clang++"
+- readelf = "${toolchain_bin}/${custom_target_triple}-readelf"
+- nm = "${toolchain_bin}/${custom_target_triple}-nm"
+- strip = "${toolchain_bin}/${custom_target_triple}-strip"
++ readelf = "${toolchain_bin}/llvm-readelf"
++ nm = "${toolchain_bin}/llvm-nm"
++ strip = "${toolchain_bin}/llvm-strip"
+
+ target_triple_flags = "--target=${custom_target_triple}"
+ sysroot_flags = "--sysroot ${custom_sysroot}"
diff --git a/attachment/repos/dart.3.4.0.patch b/attachment/repos/dart.3.4.0.patch
new file mode 100644
index 0000000000000000000000000000000000000000..332887b065fb74aa9180ff8469cc0026e4d60348
--- /dev/null
+++ b/attachment/repos/dart.3.4.0.patch
@@ -0,0 +1,8492 @@
+diff --git a/BUILD.gn b/BUILD.gn
+index 22078180b94..e5aa4e0c8f0 100644
+--- a/BUILD.gn
++++ b/BUILD.gn
+@@ -70,7 +70,7 @@ group("runtime") {
+ ]
+ }
+
+- if (is_linux || is_android) {
++ if (is_linux || is_android || is_ohos) {
+ deps += [ "runtime/bin:abstract_socket_test" ]
+ } else if (is_fuchsia) {
+ deps += [ ":fuchsia_test_package" ]
+@@ -88,7 +88,7 @@ group("runtime_precompiled") {
+ "runtime/bin:dart_precompiled_runtime",
+ "runtime/bin:process_test",
+ ]
+- if (is_linux || is_android) {
++ if (is_linux || is_android || is_ohos) {
+ deps += [ "runtime/bin:abstract_socket_test" ]
+ }
+ }
+diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
+index 551817d5e32..d606a4a4a14 100644
+--- a/build/config/compiler/BUILD.gn
++++ b/build/config/compiler/BUILD.gn
+@@ -254,7 +254,7 @@ config("compiler") {
+
+ # Linux/Android common flags setup.
+ # ---------------------------------
+- if (is_linux || is_android) {
++ if (is_linux || is_android || is_ohos) {
+ ldflags += [
+ "-Wl,-z,noexecstack",
+ "-Wl,-z,now",
+@@ -263,7 +263,7 @@ config("compiler") {
+ ]
+ }
+
+- if (is_android || is_linux || is_mac || is_fuchsia) {
++ if (is_android || is_linux || is_mac || is_fuchsia || is_ohos) {
+ if (use_flutter_cxx) {
+ # shared_library_config isn't transitive, so we don't automatically get
+ # another versions of libcxx with and without -fPIC. Properly setting this
+@@ -281,7 +281,7 @@ config("compiler") {
+
+ # Linux-specific compiler flags setup.
+ # ------------------------------------
+- if (is_linux) {
++ if (is_linux || is_ohos) {
+ if (is_clang) {
+ if (dart_sysroot == "alpine") {
+ # alpine linux target names can be found at:
+@@ -405,7 +405,7 @@ config("compiler") {
+ # changes since artifacts from an older version of the toolchain may or may
+ # not be compatible with newer ones. To achieve this, we insert a synthetic
+ # define into the compile line.
+- if (is_clang && (is_linux || is_mac) && dart_sysroot != "alpine") {
++ if (is_clang && (is_linux || is_mac || is_ohos) && dart_sysroot != "alpine") {
+ if (is_linux && host_cpu == "arm64") {
+ toolchain_stamp_file =
+ "//buildtools/linux-arm64/clang/.versions/clang.cipd_version"
+@@ -586,6 +586,11 @@ config("runtime_library") {
+ "dl",
+ "pthread",
+ ]
++ } else if (is_ohos) {
++ libs += [
++ "dl",
++ "pthread",
++ ]
+ } else if (is_android) {
+ # Android standard library setup.
+
+@@ -682,7 +687,7 @@ if (is_win) {
+
+ # The Raspberry Pi 1 toolchain enables this warning, but Dart doesn't build
+ # cleanly with it.
+- if (is_linux && !is_clang && current_cpu == "arm" && arm_version == 6) {
++ if ((is_linux || is_ohos) && !is_clang && current_cpu == "arm" && arm_version == 6) {
+ default_warning_flags += [ "-Wno-type-limits" ]
+ }
+
+diff --git a/build/config/sanitizers/sanitizers.gni b/build/config/sanitizers/sanitizers.gni
+index 2fcf3163f42..01eb25cb7c5 100644
+--- a/build/config/sanitizers/sanitizers.gni
++++ b/build/config/sanitizers/sanitizers.gni
+@@ -3,6 +3,12 @@
+ # found in the LICENSE file.
+
+ declare_args() {
++ # Use libc++ (buildtools/third_party/libc++ and
++ # buildtools/third_party/libc++abi) instead of stdlibc++ as standard library.
++ # This is intended to be used for instrumented builds.
++ use_custom_libcxx =
++ (is_asan && (is_linux || is_ohos)) || is_lsan || is_msan || is_tsan || is_ubsan
++
+ # Track where uninitialized memory originates from. From fastest to slowest:
+ # 0 - no tracking, 1 - track only the initial allocation site, 2 - track the
+ # chain of stores leading from allocation site to use site.
+diff --git a/build/config/sysroot.gni b/build/config/sysroot.gni
+index 2a6cab96af9..41a335fc8ec 100644
+--- a/build/config/sysroot.gni
++++ b/build/config/sysroot.gni
+@@ -15,7 +15,7 @@ declare_args() {
+ dart_sysroot = ""
+ }
+
+-if (is_linux) {
++if (is_linux || is_ohos) {
+ if (dart_sysroot == "alpine") {
+ if (current_cpu == "x86") {
+ target_sysroot =
+diff --git a/build/rust/rust.gni b/build/rust/rust.gni
+index 0a8cdc13970..8bdbf6a1ff5 100644
+--- a/build/rust/rust.gni
++++ b/build/rust/rust.gni
+@@ -27,6 +27,8 @@ template("rust_library") {
+ cargo_out_dir = target_out_dir
+ if (is_linux) {
+ rust_os = "unknown-linux-gnu"
++ } else if (is_ohos) {
++ rust_os = "unknown-linux-gnu"
+ } else if (is_mac) {
+ rust_os = "apple-darwin"
+ } else if (is_win) {
+diff --git a/build/sanitizers/BUILD.gn b/build/sanitizers/BUILD.gn
+index e9f8c7a55cd..b1df3aef772 100644
+--- a/build/sanitizers/BUILD.gn
++++ b/build/sanitizers/BUILD.gn
+@@ -2,7 +2,7 @@
+ # Use of this source code is governed by a BSD-style license that can be
+ # found in the LICENSE file.
+
+-if (is_linux && !is_chromeos) {
++if ((is_linux || is_ohos) && !is_chromeos) {
+ # TODO(GYP): Figure out which of these work and are needed on other platforms.
+ copy("copy_llvm_symbolizer") {
+ if (is_win) {
+diff --git a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
+index 1e8a0fbc5ef..0e000066c75 100644
+--- a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
++++ b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
+@@ -772,6 +772,8 @@ final class Double implements SizedNativeType {
+ Abi.androidIA32: Int32(),
+ Abi.androidX64: Int64(),
+ Abi.androidRiscv64: Int64(),
++ Abi.ohosArm: Int32(),
++ Abi.ohosArm64: Int64(),
+ Abi.fuchsiaArm64: Int64(),
+ Abi.fuchsiaX64: Int64(),
+ Abi.fuchsiaRiscv64: Int64(),
+@@ -923,12 +925,16 @@ final class Abi {
+ static const androidIA32 = _androidIA32;
+ static const linuxX64 = _linuxX64;
+ static const macosX64 = _macosX64;
++ static const ohosArm = _ohosArm;
++ static const ohosArm64 = _ohosArm64;
+
+ static const _androidArm = Abi._(_Architecture.arm, _OS.android);
+ static const _androidArm64 = Abi._(_Architecture.arm64, _OS.android);
+ static const _androidIA32 = Abi._(_Architecture.ia32, _OS.android);
+ static const _linuxX64 = Abi._(_Architecture.x64, _OS.linux);
+ static const _macosX64 = Abi._(_Architecture.x64, _OS.macos);
++ static const _ohosArm = Abi._(_Architecture.arm, _OS.ohos);
++ static const _ohosArm64 = Abi._(_Architecture.arm64, _OS.ohos);
+
+ final _OS _os;
+
+@@ -946,6 +952,7 @@ enum _Architecture {
+
+ enum _OS {
+ android,
++ ohos,
+ fuchsia,
+ ios,
+ linux,
+diff --git a/pkg/dds/lib/dds.dart b/pkg/dds/lib/dds.dart
+index ab4de82862f..afdca00be3a 100644
+--- a/pkg/dds/lib/dds.dart
++++ b/pkg/dds/lib/dds.dart
+@@ -45,7 +45,7 @@ abstract class DartDevelopmentService {
+ static Future startDartDevelopmentService(
+ Uri remoteVmServiceUri, {
+ Uri? serviceUri,
+- bool enableAuthCodes = true,
++ bool enableAuthCodes = false,
+ bool ipv6 = false,
+ bool enableServicePortFallback = false,
+ List cachedUserTags = const [],
+diff --git a/pkg/dds/lib/src/dap/adapters/dart.dart b/pkg/dds/lib/src/dap/adapters/dart.dart
+index ae16fe10bb8..8b6fc77dedb 100644
+--- a/pkg/dds/lib/src/dap/adapters/dart.dart
++++ b/pkg/dds/lib/src/dap/adapters/dart.dart
+@@ -480,7 +480,7 @@ abstract class DartDebugAdapter> output, {
+ this.ipv6 = false,
+ this.enableDds = true,
+- this.enableAuthCodes = true,
++ this.enableAuthCodes = false,
+ this.test = false,
+ this.logger,
+ Function? onError,
+diff --git a/pkg/dds/lib/src/dds_impl.dart b/pkg/dds/lib/src/dds_impl.dart
+index 90608678379..5f7f46a1381 100644
+--- a/pkg/dds/lib/src/dds_impl.dart
++++ b/pkg/dds/lib/src/dds_impl.dart
+@@ -72,7 +72,10 @@ class DartDevelopmentServiceImpl implements DartDevelopmentService {
+ _streamManager = StreamManager(this);
+ _packageUriConverter = PackageUriConverter(this);
+ _dapHandler = DapHandler(this);
+- _authCode = _authCodesEnabled ? _makeAuthToken() : '';
++ _authCode = _authCodesEnabled ? _makeAuthToken() : '';
++ //TODO 临时关闭
++ this._authCodesEnabled = false;
++ _authCode = "";//_authCodesEnabled ? _makeAuthToken() : '';
+ }
+
+ Future startService() async {
+@@ -451,7 +454,7 @@ class DartDevelopmentServiceImpl implements DartDevelopmentService {
+
+ @override
+ bool get authCodesEnabled => _authCodesEnabled;
+- final bool _authCodesEnabled;
++ bool _authCodesEnabled;
+ String? get authCode => _authCode;
+ String? _authCode;
+
+diff --git a/pkg/vm/lib/modular/transformations/ffi/abi.dart b/pkg/vm/lib/modular/transformations/ffi/abi.dart
+index 9ac7fc5d7a9..21fec27b961 100644
+--- a/pkg/vm/lib/modular/transformations/ffi/abi.dart
++++ b/pkg/vm/lib/modular/transformations/ffi/abi.dart
+@@ -33,6 +33,7 @@ extension on _Architecture {
+ /// The operating systems the Dart VM runs on.
+ enum _OS {
+ android,
++ ohos,
+ fuchsia,
+ ios,
+ linux,
+@@ -66,6 +67,10 @@ class Abi {
+ /// The application binary interface for Android on 64-bit RISC-V.
+ static const androidRiscv64 = _androidRiscv64;
+
++ static const ohosArm = _ohosArm;
++
++ static const ohosArm64 = _ohosArm64;
++
+ /// The application binary interface for Fuchsia on the Arm64 architecture.
+ static const fuchsiaArm64 = _fuchsiaArm64;
+
+@@ -134,6 +139,8 @@ class Abi {
+ androidIA32,
+ androidX64,
+ androidRiscv64,
++ ohosArm,
++ ohosArm64,
+ fuchsiaArm64,
+ fuchsiaX64,
+ fuchsiaRiscv64,
+@@ -178,6 +185,8 @@ class Abi {
+ static const _androidIA32 = Abi._(_Architecture.ia32, _OS.android);
+ static const _androidX64 = Abi._(_Architecture.x64, _OS.android);
+ static const _androidRiscv64 = Abi._(_Architecture.riscv64, _OS.android);
++ static const _ohosArm = Abi._(_Architecture.arm, _OS.ohos);
++ static const _ohosArm64 = Abi._(_Architecture.arm64, _OS.ohos);
+ static const _fuchsiaArm64 = Abi._(_Architecture.arm64, _OS.fuchsia);
+ static const _fuchsiaX64 = Abi._(_Architecture.x64, _OS.fuchsia);
+ static const _fuchsiaRiscv64 = Abi._(_Architecture.riscv64, _OS.fuchsia);
+@@ -204,6 +213,8 @@ const Map abiNames = {
+ Abi.androidIA32: 'androidIA32',
+ Abi.androidX64: 'androidX64',
+ Abi.androidRiscv64: 'androidRiscv64',
++ Abi.ohosArm: 'ohosArm',
++ Abi.ohosArm64: 'ohosArm64',
+ Abi.fuchsiaArm64: 'fuchsiaArm64',
+ Abi.fuchsiaX64: 'fuchsiaX64',
+ Abi.fuchsiaRiscv64: 'fuchsiaRiscv64',
+@@ -245,6 +256,7 @@ final Map wordSize =
+ const Map> nonSizeAlignment = {
+ // _wordSize64
+ Abi.androidArm64: _wordSize64,
++ Abi.ohosArm64: _wordSize64,
+ Abi.androidX64: _wordSize64,
+ Abi.androidRiscv64: _wordSize64,
+ Abi.fuchsiaArm64: _wordSize64,
+@@ -265,6 +277,7 @@ const Map> nonSizeAlignment = {
+ Abi.linuxIA32: _wordSize32Align32,
+ // _wordSize32Align64
+ Abi.androidArm: _wordSize32Align64,
++ Abi.ohosArm: _wordSize32Align64,
+ Abi.linuxArm: _wordSize32Align64,
+ Abi.linuxRiscv32: _wordSize32Align64,
+ Abi.windowsIA32: _wordSize32Align64,
+diff --git a/pkg/vm/lib/target_os.dart b/pkg/vm/lib/target_os.dart
+index 741c090aba4..d214e3d6991 100644
+--- a/pkg/vm/lib/target_os.dart
++++ b/pkg/vm/lib/target_os.dart
+@@ -7,6 +7,7 @@ enum TargetOS {
+ fuchsia('fuchsia', '/'),
+ iOS('ios', '/'),
+ linux('linux', '/'),
++ ohos('ohos', '/'),
+ macOS('macos', '/'),
+ windows('windows', '\\');
+
+diff --git a/pkg/vm/testcases/transformations/ffi/abi_specific_int.dart b/pkg/vm/testcases/transformations/ffi/abi_specific_int.dart
+index 6d4e7b0871e..436f3d03301 100644
+--- a/pkg/vm/testcases/transformations/ffi/abi_specific_int.dart
++++ b/pkg/vm/testcases/transformations/ffi/abi_specific_int.dart
+@@ -10,6 +10,8 @@ import 'dart:ffi';
+ Abi.androidIA32: Uint32(),
+ Abi.androidX64: Uint32(),
+ Abi.androidRiscv64: Int32(),
++ Abi.ohosArm: Uint32(),
++ Abi.ohosArm64: Uint32(),
+ Abi.fuchsiaArm64: Uint32(),
+ Abi.fuchsiaX64: Uint32(),
+ Abi.fuchsiaRiscv64: Uint32(),
+diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn
+index c2920b70289..f0f004534df 100644
+--- a/runtime/BUILD.gn
++++ b/runtime/BUILD.gn
+@@ -94,7 +94,7 @@ config("dart_precompiler_config") {
+
+ config("dart_os_config") {
+ defines = []
+-
++ print("DART TARGET OS: "+target_os )
+ if (target_os == "android") {
+ defines += [ "DART_TARGET_OS_ANDROID" ]
+ } else if (target_os == "fuchsia") {
+@@ -104,6 +104,11 @@ config("dart_os_config") {
+ defines += [ "DART_TARGET_OS_MACOS_IOS" ]
+ } else if (target_os == "linux") {
+ defines += [ "DART_TARGET_OS_LINUX" ]
++ } else if (target_os == "ohos") {
++ defines += [ "DART_TARGET_OS_OHOS" ]
++ if (is_ohos) {
++ defines += [ "DART_RUNTIME_OS_OHOS" ]
++ }
+ } else if (target_os == "mac") {
+ defines += [ "DART_TARGET_OS_MACOS" ]
+ } else if (target_os == "win") {
+@@ -221,6 +226,7 @@ config("dart_config") {
+ "-ggdb3",
+ "-fno-rtti",
+ "-fno-exceptions",
++ "-Wno-sign-compare"
+ ]
+ if (is_clang) {
+ cflags += [
+@@ -249,6 +255,11 @@ config("dart_config") {
+ cflags += [ "-O${dart_default_optimization_level}" ]
+ }
+
++
++ if (is_ohos) {
++ ldflags += ["-lhilog_ndk.z"]
++ }
++
+ if (is_fuchsia) {
+ # safe-stack does not work with the interpreter.
+ cflags += [
+diff --git a/runtime/bin/BUILD.gn b/runtime/bin/BUILD.gn
+index 52b9bdba29e..08547c0029f 100644
+--- a/runtime/bin/BUILD.gn
++++ b/runtime/bin/BUILD.gn
+@@ -28,6 +28,16 @@ config("libdart_builtin_config") {
+ "log",
+ ]
+ }
++
++ if (is_linux && is_ohos) {
++ is_linux = false
++ }
++
++ if(is_ohos && !is_win){
++ libs +=[
++ "hilog_ndk.z"
++ ]
++ }
+ }
+
+ config("export_api_symbols") {
+@@ -450,7 +460,7 @@ template("dart_io") {
+ ]
+ }
+
+- if (is_linux || is_win || is_fuchsia) {
++ if (is_linux || is_win || is_fuchsia || is_ohos) {
+ if (dart_use_fallback_root_certificates) {
+ deps += [ "../../third_party/fallback_root_certificates" ]
+ } else {
+@@ -1007,6 +1017,15 @@ shared_library("entrypoints_verification_test") {
+ # The only effect of DART_SHARED_LIB is to export the Dart API.
+ "DART_SHARED_LIB",
+ ]
++ if (is_linux || is_android || (is_ohos || !is_win)) {
++ cflags = [ "-fPIC" ]
++ }
++ if (is_win) {
++ # TODO(dartbug.com/40579): This wrongly links in dart.exe on precompiled.
++ libs = [ "dart.lib" ]
++ abs_root_out_dir = rebase_path(root_out_dir)
++ ldflags = [ "/LIBPATH:$abs_root_out_dir" ]
++ }
+ }
+
+ shared_library("ffi_test_dynamic_library") {
+@@ -1017,6 +1036,9 @@ shared_library("ffi_test_dynamic_library") {
+ # The only effect of DART_SHARED_LIB is to export the Dart API.
+ "DART_SHARED_LIB",
+ ]
++ if (is_linux || is_android || is_ohos) {
++ cflags = [ "-fPIC" ]
++ }
+ }
+
+ shared_library("ffi_test_functions") {
+@@ -1045,6 +1067,9 @@ shared_library("ffi_test_functions") {
+ # The only effect of DART_SHARED_LIB is to export the Dart API.
+ "DART_SHARED_LIB",
+ ]
++ if (is_linux || is_android || is_ohos) {
++ cflags = [ "-fPIC" ]
++ }
+ if (is_win) {
+ # TODO(dartbug.com/40579): This wrongly links in dart.exe on precompiled.
+ libs = [ "dart.lib" ]
+diff --git a/runtime/bin/abstract_socket_test.cc b/runtime/bin/abstract_socket_test.cc
+index f9841b8655e..153f966ba3d 100644
+--- a/runtime/bin/abstract_socket_test.cc
++++ b/runtime/bin/abstract_socket_test.cc
+@@ -8,7 +8,7 @@
+ // closes the connection and UNIX socket.
+
+ #include "platform/globals.h"
+-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS)
+
+ #include
+ #include
+@@ -101,4 +101,4 @@ int main() {
+ return -1;
+ }
+
+-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS)
+diff --git a/runtime/bin/address_sanitizer.cc b/runtime/bin/address_sanitizer.cc
+index 4b8776df7a5..5f3b9ce4f2f 100644
+--- a/runtime/bin/address_sanitizer.cc
++++ b/runtime/bin/address_sanitizer.cc
+@@ -4,7 +4,7 @@
+
+ #include "platform/globals.h"
+
+-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOSX)
++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOSX) || defined(DART_HOST_OS_OHOS)
+ #if defined(__has_feature)
+ #if __has_feature(address_sanitizer)
+ #if !defined(GOOGLE3)
+@@ -25,4 +25,4 @@ __asan_default_options() {
+ #endif // !defined(GOOGLE3)
+ #endif // __has_feature(address_sanitizer)
+ #endif // defined(__has_feature)
+-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOSX)
++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOSX) || defined(DART_HOST_OS_OHOS)
+diff --git a/runtime/bin/analyze_snapshot.cc b/runtime/bin/analyze_snapshot.cc
+index 6c38f9e589a..6320d28dbb7 100644
+--- a/runtime/bin/analyze_snapshot.cc
++++ b/runtime/bin/analyze_snapshot.cc
+@@ -10,7 +10,7 @@
+ #include "bin/platform.h"
+
+ #if defined(TARGET_ARCH_IS_64_BIT) && defined(DART_PRECOMPILED_RUNTIME) && \
+- (defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_LINUX))
++ (defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_OHOS))
+ #define SUPPORT_ANALYZE_SNAPSHOT
+ #endif
+
+diff --git a/runtime/bin/builtin_impl_sources.gni b/runtime/bin/builtin_impl_sources.gni
+index 0db1b7655bc..0a3a7e4d2ed 100644
+--- a/runtime/bin/builtin_impl_sources.gni
++++ b/runtime/bin/builtin_impl_sources.gni
+@@ -13,12 +13,14 @@ builtin_impl_sources = [
+ "crypto_linux.cc",
+ "crypto_macos.cc",
+ "crypto_win.cc",
++ "crypto_ohos.cc",
+ "dartutils.cc",
+ "dartutils.h",
+ "directory.cc",
+ "directory.h",
+ "directory_fuchsia.cc",
+ "directory_linux.cc",
++ "directory_ohos.cc",
+ "directory_macos.cc",
+ "directory_win.cc",
+ "exe_utils.cc",
+@@ -27,10 +29,12 @@ builtin_impl_sources = [
+ "fdutils_fuchsia.cc",
+ "fdutils_linux.cc",
+ "fdutils_macos.cc",
++ "fdutils_ohos.cc",
+ "file.cc",
+ "file.h",
+ "file_fuchsia.cc",
+ "file_linux.cc",
++ "file_ohos.cc",
+ "file_macos.cc",
+ "file_support.cc",
+ "file_win.cc",
+@@ -51,9 +55,12 @@ builtin_impl_sources = [
+ "thread_macos.h",
+ "thread_win.cc",
+ "thread_win.h",
++ "thread_ohos.cc",
++ "thread_ohos.h",
+ "utils.cc",
+ "utils.h",
+ "utils_fuchsia.cc",
++ "utils_ohos.cc",
+ "utils_linux.cc",
+ "utils_macos.cc",
+ "utils_win.cc",
+diff --git a/runtime/bin/builtin_natives.cc b/runtime/bin/builtin_natives.cc
+index bd28a128a22..f5b2cb89618 100644
+--- a/runtime/bin/builtin_natives.cc
++++ b/runtime/bin/builtin_natives.cc
+@@ -18,6 +18,11 @@
+ #include "bin/io_natives.h"
+ #include "bin/platform.h"
+
++#ifdef DART_HOST_OS_OHOS
++#include
++#include
++#endif
++
+ namespace dart {
+ namespace bin {
+
+@@ -108,6 +113,13 @@ void FUNCTION_NAME(Builtin_PrintString)(Dart_NativeArguments args) {
+ Dart_ServiceSendDataEvent("Stdout", "WriteEvent", chars, new_length);
+ ASSERT(res == nullptr);
+ }
++
++#ifdef DART_HOST_OS_OHOS
++ {
++ pthread_t thread = pthread_self();
++ OH_LOG_Print(LOG_APP,LOG_INFO,LOG_DOMAIN,"XComDartVm","Thread:%{public}lu %{public}s",thread,chars);
++ }
++#endif
+ }
+
+ } // namespace bin
+diff --git a/runtime/bin/console_posix.cc b/runtime/bin/console_posix.cc
+index f318ae1ef6a..59c2b6c6509 100644
+--- a/runtime/bin/console_posix.cc
++++ b/runtime/bin/console_posix.cc
+@@ -4,7 +4,7 @@
+
+ #include "platform/globals.h"
+ #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
+- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA)
++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS)
+
+ #include "bin/console.h"
+
+@@ -28,4 +28,4 @@ void Console::RestoreConfig() {
+ } // namespace dart
+
+ #endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
+- // defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA)
++ // defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS)
+diff --git a/runtime/bin/crypto_ohos.cc b/runtime/bin/crypto_ohos.cc
+new file mode 100644
+index 00000000000..b7aca695fb7
+--- /dev/null
++++ b/runtime/bin/crypto_ohos.cc
+@@ -0,0 +1,44 @@
++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
++// for details. All rights reserved. Use of this source code is governed by a
++// BSD-style license that can be found in the LICENSE file.
++
++#include "platform/globals.h"
++#if defined(DART_HOST_OS_OHOS)
++
++#include // NOLINT
++#include // NOLINT
++
++#include "bin/crypto.h"
++#include "bin/fdutils.h"
++#include "platform/signal_blocker.h"
++
++namespace dart {
++namespace bin {
++
++bool Crypto::GetRandomBytes(intptr_t count, uint8_t* buffer) {
++ ThreadSignalBlocker signal_blocker(SIGPROF);
++ intptr_t fd = TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER(
++ open("/dev/urandom", O_RDONLY | O_CLOEXEC));
++ if (fd < 0) {
++ return false;
++ }
++ intptr_t bytes_read = 0;
++ do {
++ int res = TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER(
++ read(fd, buffer + bytes_read, count - bytes_read));
++ if (res < 0) {
++ int err = errno;
++ close(fd);
++ errno = err;
++ return false;
++ }
++ bytes_read += res;
++ } while (bytes_read < count);
++ close(fd);
++ return true;
++}
++
++} // namespace bin
++} // namespace dart
++
++#endif // defined(DART_HOST_OS_OHOS)
+diff --git a/runtime/bin/directory_ohos.cc b/runtime/bin/directory_ohos.cc
+new file mode 100644
+index 00000000000..66363324364
+--- /dev/null
++++ b/runtime/bin/directory_ohos.cc
+@@ -0,0 +1,513 @@
++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
++// for details. All rights reserved. Use of this source code is governed by a
++// BSD-style license that can be found in the LICENSE file.
++
++#include "platform/globals.h"
++#if defined(DART_HOST_OS_OHOS)
++
++#include "bin/directory.h"
++
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++
++#include "bin/crypto.h"
++#include "bin/dartutils.h"
++#include "bin/fdutils.h"
++#include "bin/file.h"
++#include "bin/namespace.h"
++#include "bin/platform.h"
++#include "platform/signal_blocker.h"
++
++namespace dart {
++namespace bin {
++
++PathBuffer::PathBuffer() : length_(0) {
++ data_ = calloc(PATH_MAX + 1, sizeof(char)); // NOLINT
++}
++
++PathBuffer::~PathBuffer() {
++ free(data_);
++}
++
++bool PathBuffer::AddW(const wchar_t* name) {
++ UNREACHABLE();
++ return false;
++}
++
++char* PathBuffer::AsString() const {
++ return reinterpret_cast(data_);
++}
++
++wchar_t* PathBuffer::AsStringW() const {
++ UNREACHABLE();
++ return NULL;
++}
++
++const char* PathBuffer::AsScopedString() const {
++ return DartUtils::ScopedCopyCString(AsString());
++}
++
++bool PathBuffer::Add(const char* name) {
++ char* data = AsString();
++ int written = snprintf(data + length_, PATH_MAX - length_, "%s", name);
++ data[PATH_MAX] = '\0';
++ if ((written <= PATH_MAX - length_) && (written >= 0) &&
++ (static_cast(written) == strnlen(name, PATH_MAX + 1))) {
++ length_ += written;
++ return true;
++ } else {
++ errno = ENAMETOOLONG;
++ return false;
++ }
++}
++
++void PathBuffer::Reset(intptr_t new_length) {
++ length_ = new_length;
++ AsString()[length_] = '\0';
++}
++
++// A linked list of symbolic links, with their unique file system identifiers.
++// These are scanned to detect loops while doing a recursive directory listing.
++struct LinkList {
++ dev_t dev;
++ ino64_t ino;
++ LinkList* next;
++};
++
++ListType DirectoryListingEntry::Next(DirectoryListing* listing) {
++ if (done_) {
++ return kListDone;
++ }
++
++ if (fd_ == -1) {
++ ASSERT(lister_ == 0);
++ NamespaceScope ns(listing->namespc(), listing->path_buffer().AsString());
++ const int listingfd =
++ TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), O_DIRECTORY));
++ if (listingfd < 0) {
++ done_ = true;
++ return kListError;
++ }
++ fd_ = listingfd;
++ }
++
++ if (lister_ == 0) {
++ do {
++ lister_ = reinterpret_cast(fdopendir(fd_));
++ } while ((lister_ == 0) && (errno == EINTR));
++ if (lister_ == 0) {
++ done_ = true;
++ return kListError;
++ }
++ if (parent_ != NULL) {
++ if (!listing->path_buffer().Add(File::PathSeparator())) {
++ return kListError;
++ }
++ }
++ path_length_ = listing->path_buffer().length();
++ }
++ // Reset.
++ listing->path_buffer().Reset(path_length_);
++ ResetLink();
++
++ // Iterate the directory and post the directories and files to the
++ // ports.
++ errno = 0;
++ dirent* entry = readdir(reinterpret_cast(lister_));
++ if (entry != NULL) {
++ if (!listing->path_buffer().Add(entry->d_name)) {
++ done_ = true;
++ return kListError;
++ }
++ switch (entry->d_type) {
++ case DT_DIR:
++ if ((strcmp(entry->d_name, ".") == 0) ||
++ (strcmp(entry->d_name, "..") == 0)) {
++ return Next(listing);
++ }
++ return kListDirectory;
++ case DT_BLK:
++ case DT_CHR:
++ case DT_FIFO:
++ case DT_SOCK:
++ case DT_REG:
++ return kListFile;
++ case DT_LNK:
++ if (!listing->follow_links()) {
++ return kListLink;
++ }
++ // Else fall through to next case.
++ FALL_THROUGH;
++ case DT_UNKNOWN: {
++ // On some file systems the entry type is not determined by
++ // readdir. For those and for links we use stat to determine
++ // the actual entry type. Notice that stat returns the type of
++ // the file pointed to.
++ NamespaceScope ns(listing->namespc(),
++ listing->path_buffer().AsString());
++ struct stat64 entry_info;
++ int stat_success;
++ stat_success = TEMP_FAILURE_RETRY(
++ fstatat64(ns.fd(), ns.path(), &entry_info, AT_SYMLINK_NOFOLLOW));
++ if (stat_success == -1) {
++ return kListError;
++ }
++ if (listing->follow_links() && S_ISLNK(entry_info.st_mode)) {
++ // Check to see if we are in a loop created by a symbolic link.
++ LinkList current_link = {entry_info.st_dev, entry_info.st_ino, link_};
++ LinkList* previous = link_;
++ while (previous != NULL) {
++ if ((previous->dev == current_link.dev) &&
++ (previous->ino == current_link.ino)) {
++ // Report the looping link as a link, rather than following it.
++ return kListLink;
++ }
++ previous = previous->next;
++ }
++ stat_success =
++ TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &entry_info, 0));
++ if (stat_success == -1 || (S_IFMT & entry_info.st_mode) == 0) {
++ // Report a broken link as a link, even if follow_links is true.
++ // A symbolic link can potentially point to an anon_inode. For
++ // example, an epoll file descriptor will have a symbolic link whose
++ // content is the string anon_inode:[eventpoll]. In this case, the
++ // target doesn't belong to any regular file catogory.
++ return kListLink;
++ }
++ if (S_ISDIR(entry_info.st_mode)) {
++ // Recurse into the subdirectory with current_link added to the
++ // linked list of seen file system links.
++ link_ = new LinkList(current_link);
++ if ((strcmp(entry->d_name, ".") == 0) ||
++ (strcmp(entry->d_name, "..") == 0)) {
++ return Next(listing);
++ }
++ return kListDirectory;
++ }
++ }
++ if (S_ISDIR(entry_info.st_mode)) {
++ if ((strcmp(entry->d_name, ".") == 0) ||
++ (strcmp(entry->d_name, "..") == 0)) {
++ return Next(listing);
++ }
++ return kListDirectory;
++ } else if (S_ISLNK(entry_info.st_mode)) {
++ return kListLink;
++ } else {
++ // Regular files, character devices, block devices, fifos, sockets and
++ // unknown types are all considered as files.
++ return kListFile;
++ }
++ }
++
++ default:
++ // We should have covered all the bases. If not, let's get an error.
++ FATAL("Unexpected d_type: %d\n", entry->d_type);
++ return kListError;
++ }
++ }
++ done_ = true;
++
++ if (errno != 0) {
++ return kListError;
++ }
++
++ return kListDone;
++}
++
++DirectoryListingEntry::~DirectoryListingEntry() {
++ ResetLink();
++ if (lister_ != 0) {
++ // This also closes fd_.
++ VOID_NO_RETRY_EXPECTED(closedir(reinterpret_cast(lister_)));
++ }
++}
++
++void DirectoryListingEntry::ResetLink() {
++ if ((link_ != NULL) && ((parent_ == NULL) || (parent_->link_ != link_))) {
++ delete link_;
++ link_ = NULL;
++ }
++ if (parent_ != NULL) {
++ link_ = parent_->link_;
++ }
++}
++
++static bool DeleteRecursively(int dirfd, PathBuffer* path);
++
++static bool DeleteFile(int dirfd, char* file_name, PathBuffer* path) {
++ return path->Add(file_name) &&
++ (NO_RETRY_EXPECTED(unlinkat(dirfd, path->AsString(), 0)) == 0);
++}
++
++static bool DeleteDir(int dirfd, char* dir_name, PathBuffer* path) {
++ if ((strcmp(dir_name, ".") == 0) || (strcmp(dir_name, "..") == 0)) {
++ return true;
++ }
++ return path->Add(dir_name) && DeleteRecursively(dirfd, path);
++}
++
++static bool DeleteRecursively(int dirfd, PathBuffer* path) {
++ // Do not recurse into links for deletion. Instead delete the link.
++ // If it's a file, delete it.
++ struct stat64 st;
++ if (TEMP_FAILURE_RETRY(
++ fstatat64(dirfd, path->AsString(), &st, AT_SYMLINK_NOFOLLOW)) == -1) {
++ return false;
++ } else if (!S_ISDIR(st.st_mode)) {
++ return (NO_RETRY_EXPECTED(unlinkat(dirfd, path->AsString(), 0)) == 0);
++ }
++
++ if (!path->Add(File::PathSeparator())) {
++ return false;
++ }
++
++ // Not a link. Attempt to open as a directory and recurse into the
++ // directory.
++ const int fd =
++ TEMP_FAILURE_RETRY(openat64(dirfd, path->AsString(), O_DIRECTORY));
++ if (fd < 0) {
++ return false;
++ }
++ DIR* dir_pointer;
++ do {
++ dir_pointer = fdopendir(fd);
++ } while ((dir_pointer == NULL) && (errno == EINTR));
++ if (dir_pointer == NULL) {
++ FDUtils::SaveErrorAndClose(fd);
++ return false;
++ }
++
++ // Iterate the directory and delete all files and directories.
++ int path_length = path->length();
++ while (true) {
++ // In case `readdir()` returns `NULL` we distinguish between end-of-stream
++ // and error by looking if `errno` was updated.
++ errno = 0;
++ // In glibc 2.24+, readdir_r is deprecated.
++ // According to the man page for readdir:
++ // "readdir(3) is not required to be thread-safe. However, in modern
++ // implementations (including the glibc implementation), concurrent calls to
++ // readdir(3) that specify different directory streams are thread-safe."
++ dirent* entry = readdir(dir_pointer);
++ if (entry == NULL) {
++ // Failed to read next directory entry.
++ if (errno != 0) {
++ break;
++ }
++ // End of directory.
++ int status = NO_RETRY_EXPECTED(closedir(dir_pointer));
++ if (status != 0) {
++ return false;
++ }
++ status =
++ NO_RETRY_EXPECTED(unlinkat(dirfd, path->AsString(), AT_REMOVEDIR));
++ return status == 0;
++ }
++ bool ok = false;
++ switch (entry->d_type) {
++ case DT_DIR:
++ ok = DeleteDir(dirfd, entry->d_name, path);
++ break;
++ case DT_BLK:
++ case DT_CHR:
++ case DT_FIFO:
++ case DT_SOCK:
++ case DT_REG:
++ case DT_LNK:
++ // Treat all links as files. This will delete the link which
++ // is what we want no matter if the link target is a file or a
++ // directory.
++ ok = DeleteFile(dirfd, entry->d_name, path);
++ break;
++ case DT_UNKNOWN: {
++ if (!path->Add(entry->d_name)) {
++ break;
++ }
++ // On some file systems the entry type is not determined by
++ // readdir. For those we use lstat to determine the entry
++ // type.
++ struct stat64 entry_info;
++ if (TEMP_FAILURE_RETRY(fstatat64(dirfd, path->AsString(), &entry_info,
++ AT_SYMLINK_NOFOLLOW)) == -1) {
++ break;
++ }
++ path->Reset(path_length);
++ if (S_ISDIR(entry_info.st_mode)) {
++ ok = DeleteDir(dirfd, entry->d_name, path);
++ } else {
++ // Treat links as files. This will delete the link which is
++ // what we want no matter if the link target is a file or a
++ // directory.
++ ok = DeleteFile(dirfd, entry->d_name, path);
++ }
++ break;
++ }
++ default:
++ // We should have covered all the bases. If not, let's get an error.
++ FATAL("Unexpected d_type: %d\n", entry->d_type);
++ break;
++ }
++ if (!ok) {
++ break;
++ }
++ path->Reset(path_length);
++ }
++ // Only happens if an error.
++ ASSERT(errno != 0);
++ int err = errno;
++ VOID_NO_RETRY_EXPECTED(closedir(dir_pointer));
++ errno = err;
++ return false;
++}
++
++Directory::ExistsResult Directory::Exists(Namespace* namespc,
++ const char* dir_name) {
++ NamespaceScope ns(namespc, dir_name);
++ struct stat64 entry_info;
++ int success =
++ TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &entry_info, 0));
++ if (success == 0) {
++ if (S_ISDIR(entry_info.st_mode)) {
++ return EXISTS;
++ } else {
++ // An OSError may be constructed based on the return value of this
++ // function, so set errno to something that makes sense.
++ errno = ENOTDIR;
++ return DOES_NOT_EXIST;
++ }
++ } else {
++ if ((errno == EACCES) || (errno == EBADF) || (errno == EFAULT) ||
++ (errno == ENOMEM) || (errno == EOVERFLOW)) {
++ // Search permissions denied for one of the directories in the
++ // path or a low level error occurred. We do not know if the
++ // directory exists.
++ return UNKNOWN;
++ }
++ ASSERT((errno == ELOOP) || (errno == ENAMETOOLONG) || (errno == ENOENT) ||
++ (errno == ENOTDIR));
++ return DOES_NOT_EXIST;
++ }
++}
++
++char* Directory::CurrentNoScope() {
++ return getcwd(NULL, 0);
++}
++
++bool Directory::Create(Namespace* namespc, const char* dir_name) {
++ NamespaceScope ns(namespc, dir_name);
++ // Create the directory with the permissions specified by the
++ // process umask.
++ const int result = NO_RETRY_EXPECTED(mkdirat(ns.fd(), ns.path(), 0777));
++ // If the directory already exists, treat it as a success.
++ if ((result == -1) && (errno == EEXIST)) {
++ return (Exists(namespc, dir_name) == EXISTS);
++ }
++ return (result == 0);
++}
++
++const char* Directory::SystemTemp(Namespace* namespc) {
++ if (Directory::system_temp_path_override_ != NULL) {
++ return DartUtils::ScopedCopyCString(Directory::system_temp_path_override_);
++ }
++
++ PathBuffer path;
++ const char* temp_dir = getenv("TMPDIR");
++ if (temp_dir == NULL) {
++ temp_dir = getenv("TMP");
++ }
++ if (temp_dir == NULL) {
++ temp_dir = "/tmp";
++ }
++ NamespaceScope ns(namespc, temp_dir);
++ if (!path.Add(ns.path())) {
++ return NULL;
++ }
++
++ // Remove any trailing slash.
++ char* result = path.AsString();
++ int length = strlen(result);
++ if ((length > 1) && (result[length - 1] == '/')) {
++ result[length - 1] = '\0';
++ }
++ return path.AsScopedString();
++}
++
++// Returns a new, unused directory name, adding characters to the end
++// of prefix. Creates the directory with the permissions specified
++// by the process umask.
++// The return value is Dart_ScopeAllocated.
++const char* Directory::CreateTemp(Namespace* namespc, const char* prefix) {
++ PathBuffer path;
++ const int firstchar = 'A';
++ const int numchars = 'Z' - 'A' + 1;
++ uint8_t random_bytes[7];
++
++ // mkdtemp doesn't have an "at" variant, so we have to simulate it.
++ if (!path.Add(prefix)) {
++ return NULL;
++ }
++ intptr_t prefix_length = path.length();
++ while (true) {
++ Crypto::GetRandomBytes(6, random_bytes);
++ for (intptr_t i = 0; i < 6; i++) {
++ random_bytes[i] = (random_bytes[i] % numchars) + firstchar;
++ }
++ random_bytes[6] = '\0';
++ if (!path.Add(reinterpret_cast(random_bytes))) {
++ return NULL;
++ }
++ NamespaceScope ns(namespc, path.AsString());
++ const int result = NO_RETRY_EXPECTED(mkdirat(ns.fd(), ns.path(), 0777));
++ if (result == 0) {
++ return path.AsScopedString();
++ } else if (errno == EEXIST) {
++ path.Reset(prefix_length);
++ } else {
++ return NULL;
++ }
++ }
++}
++
++bool Directory::Delete(Namespace* namespc,
++ const char* dir_name,
++ bool recursive) {
++ NamespaceScope ns(namespc, dir_name);
++ if (!recursive) {
++ if ((File::GetType(namespc, dir_name, false) == File::kIsLink) &&
++ (File::GetType(namespc, dir_name, true) == File::kIsDirectory)) {
++ return NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), 0)) == 0;
++ }
++ return NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), AT_REMOVEDIR)) == 0;
++ } else {
++ PathBuffer path;
++ if (!path.Add(ns.path())) {
++ return false;
++ }
++ return DeleteRecursively(ns.fd(), &path);
++ }
++}
++
++bool Directory::Rename(Namespace* namespc,
++ const char* old_path,
++ const char* new_path) {
++ ExistsResult exists = Exists(namespc, old_path);
++ if (exists != EXISTS) {
++ return false;
++ }
++ NamespaceScope oldns(namespc, old_path);
++ NamespaceScope newns(namespc, new_path);
++ return (NO_RETRY_EXPECTED(renameat(oldns.fd(), oldns.path(), newns.fd(),
++ newns.path())) == 0);
++}
++
++} // namespace bin
++} // namespace dart
++
++#endif // defined(DART_HOST_OS_OHOS)
+diff --git a/runtime/bin/eventhandler.h b/runtime/bin/eventhandler.h
+index d35fc539ab1..b49bdbc705c 100644
+--- a/runtime/bin/eventhandler.h
++++ b/runtime/bin/eventhandler.h
+@@ -593,6 +593,8 @@ class DescriptorInfoMultipleMixin : public DI {
+ #include "bin/eventhandler_macos.h"
+ #elif defined(DART_HOST_OS_WINDOWS)
+ #include "bin/eventhandler_win.h"
++#elif defined(DART_HOST_OS_OHOS)
++#include "bin/eventhandler_ohos.h"
+ #else
+ #error Unknown target os.
+ #endif
+diff --git a/runtime/bin/eventhandler_ohos.cc b/runtime/bin/eventhandler_ohos.cc
+new file mode 100644
+index 00000000000..38d769d435b
+--- /dev/null
++++ b/runtime/bin/eventhandler_ohos.cc
+@@ -0,0 +1,443 @@
++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
++// for details. All rights reserved. Use of this source code is governed by a
++// BSD-style license that can be found in the LICENSE file.
++
++#include "platform/globals.h"
++#if defined(DART_HOST_OS_OHOS)
++
++#include "bin/eventhandler.h"
++#include "bin/eventhandler_ohos.h"
++
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++
++#include "bin/dartutils.h"
++#include "bin/fdutils.h"
++#include "bin/lockers.h"
++#include "bin/process.h"
++#include "bin/socket.h"
++#include "bin/thread.h"
++#include "platform/syslog.h"
++#include "platform/utils.h"
++
++namespace dart {
++namespace bin {
++
++intptr_t DescriptorInfo::GetPollEvents() {
++ // Do not ask for EPOLLERR and EPOLLHUP explicitly as they are
++ // triggered anyway.
++ intptr_t events = 0;
++ if ((Mask() & (1 << kInEvent)) != 0) {
++ events |= EPOLLIN;
++ }
++ if ((Mask() & (1 << kOutEvent)) != 0) {
++ events |= EPOLLOUT;
++ }
++ return events;
++}
++
++// Unregister the file descriptor for a DescriptorInfo structure with
++// epoll.
++static void RemoveFromEpollInstance(intptr_t epoll_fd_, DescriptorInfo* di) {
++ VOID_NO_RETRY_EXPECTED(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, di->fd(), NULL));
++}
++
++static void AddToEpollInstance(intptr_t epoll_fd_, DescriptorInfo* di) {
++ struct epoll_event event;
++ event.events = EPOLLRDHUP | di->GetPollEvents();
++ if (!di->IsListeningSocket()) {
++ event.events |= EPOLLET;
++ }
++ event.data.ptr = di;
++ int status =
++ NO_RETRY_EXPECTED(epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, di->fd(), &event));
++ if (status == -1) {
++ // TODO(dart:io): Verify that the dart end is handling this correctly.
++
++ // Epoll does not accept the file descriptor. It could be due to
++ // already closed file descriptor, or unuspported devices, such
++ // as /dev/null. In such case, mark the file descriptor as closed,
++ // so dart will handle it accordingly.
++ di->NotifyAllDartPorts(1 << kCloseEvent);
++ }
++}
++
++EventHandlerImplementation::EventHandlerImplementation()
++ : socket_map_(&SimpleHashMap::SamePointerValue, 16) {
++ intptr_t result;
++ result = NO_RETRY_EXPECTED(pipe(interrupt_fds_));
++ if (result != 0) {
++ FATAL("Pipe creation failed");
++ }
++ if (!FDUtils::SetNonBlocking(interrupt_fds_[0])) {
++ FATAL("Failed to set pipe fd non blocking\n");
++ }
++ if (!FDUtils::SetCloseOnExec(interrupt_fds_[0])) {
++ FATAL("Failed to set pipe fd close on exec\n");
++ }
++ if (!FDUtils::SetCloseOnExec(interrupt_fds_[1])) {
++ FATAL("Failed to set pipe fd close on exec\n");
++ }
++ shutdown_ = false;
++ // The initial size passed to epoll_create is ignore on newer (>=
++ // 2.6.8) Linux versions
++ static const int kEpollInitialSize = 64;
++ epoll_fd_ = NO_RETRY_EXPECTED(epoll_create(kEpollInitialSize));
++ if (epoll_fd_ == -1) {
++ FATAL("Failed creating epoll file descriptor: %i", errno);
++ }
++ if (!FDUtils::SetCloseOnExec(epoll_fd_)) {
++ FATAL("Failed to set epoll fd close on exec\n");
++ }
++ // Register the interrupt_fd with the epoll instance.
++ struct epoll_event event;
++ event.events = EPOLLIN;
++ event.data.ptr = NULL;
++ int status = NO_RETRY_EXPECTED(
++ epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, interrupt_fds_[0], &event));
++ if (status == -1) {
++ FATAL("Failed adding interrupt fd to epoll instance");
++ }
++ timer_fd_ = NO_RETRY_EXPECTED(timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC));
++ if (timer_fd_ == -1) {
++ FATAL("Failed creating timerfd file descriptor: %i", errno);
++ }
++ // Register the timer_fd_ with the epoll instance.
++ event.events = EPOLLIN;
++ event.data.fd = timer_fd_;
++ status =
++ NO_RETRY_EXPECTED(epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, timer_fd_, &event));
++ if (status == -1) {
++ FATAL("Failed adding timerfd fd(%i) to epoll instance: %i", timer_fd_,
++ errno);
++ }
++}
++
++static void DeleteDescriptorInfo(void* info) {
++ DescriptorInfo* di = reinterpret_cast(info);
++ di->Close();
++ delete di;
++}
++
++EventHandlerImplementation::~EventHandlerImplementation() {
++ socket_map_.Clear(DeleteDescriptorInfo);
++ close(epoll_fd_);
++ close(timer_fd_);
++ close(interrupt_fds_[0]);
++ close(interrupt_fds_[1]);
++}
++
++void EventHandlerImplementation::UpdateEpollInstance(intptr_t old_mask,
++ DescriptorInfo* di) {
++ intptr_t new_mask = di->Mask();
++ if ((old_mask != 0) && (new_mask == 0)) {
++ RemoveFromEpollInstance(epoll_fd_, di);
++ } else if ((old_mask == 0) && (new_mask != 0)) {
++ AddToEpollInstance(epoll_fd_, di);
++ } else if ((old_mask != 0) && (new_mask != 0) && (old_mask != new_mask)) {
++ ASSERT(!di->IsListeningSocket());
++ RemoveFromEpollInstance(epoll_fd_, di);
++ AddToEpollInstance(epoll_fd_, di);
++ }
++}
++
++DescriptorInfo* EventHandlerImplementation::GetDescriptorInfo(
++ intptr_t fd,
++ bool is_listening) {
++ ASSERT(fd >= 0);
++ SimpleHashMap::Entry* entry = socket_map_.Lookup(
++ GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd), true);
++ ASSERT(entry != NULL);
++ DescriptorInfo* di = reinterpret_cast(entry->value);
++ if (di == NULL) {
++ // If there is no data in the hash map for this file descriptor a
++ // new DescriptorInfo for the file descriptor is inserted.
++ if (is_listening) {
++ di = new DescriptorInfoMultiple(fd);
++ } else {
++ di = new DescriptorInfoSingle(fd);
++ }
++ entry->value = di;
++ }
++ ASSERT(fd == di->fd());
++ return di;
++}
++
++void EventHandlerImplementation::WakeupHandler(intptr_t id,
++ Dart_Port dart_port,
++ int64_t data) {
++ InterruptMessage msg;
++ msg.id = id;
++ msg.dart_port = dart_port;
++ msg.data = data;
++ // WriteToBlocking will write up to 512 bytes atomically, and since our msg
++ // is smaller than 512, we don't need a thread lock.
++ // See: http://linux.die.net/man/7/pipe, section 'Pipe_buf'.
++ ASSERT(kInterruptMessageSize < PIPE_BUF);
++ intptr_t result =
++ FDUtils::WriteToBlocking(interrupt_fds_[1], &msg, kInterruptMessageSize);
++ if (result != kInterruptMessageSize) {
++ if (result == -1) {
++ perror("Interrupt message failure:");
++ }
++ FATAL("Interrupt message failure. Wrote %" Pd " bytes.", result);
++ }
++}
++
++void EventHandlerImplementation::HandleInterruptFd() {
++ const intptr_t MAX_MESSAGES = kInterruptMessageSize;
++ InterruptMessage msg[MAX_MESSAGES];
++ ssize_t bytes = TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER(
++ read(interrupt_fds_[0], msg, MAX_MESSAGES * kInterruptMessageSize));
++ for (ssize_t i = 0; i < bytes / kInterruptMessageSize; i++) {
++ if (msg[i].id == kTimerId) {
++ timeout_queue_.UpdateTimeout(msg[i].dart_port, msg[i].data);
++ UpdateTimerFd();
++ } else if (msg[i].id == kShutdownId) {
++ shutdown_ = true;
++ } else {
++ ASSERT((msg[i].data & COMMAND_MASK) != 0);
++ Socket* socket = reinterpret_cast(msg[i].id);
++ RefCntReleaseScope rs(socket);
++ if (socket->fd() == -1) {
++ continue;
++ }
++ DescriptorInfo* di =
++ GetDescriptorInfo(socket->fd(), IS_LISTENING_SOCKET(msg[i].data));
++ if (IS_COMMAND(msg[i].data, kShutdownReadCommand)) {
++ ASSERT(!di->IsListeningSocket());
++ // Close the socket for reading.
++ VOID_NO_RETRY_EXPECTED(shutdown(di->fd(), SHUT_RD));
++ } else if (IS_COMMAND(msg[i].data, kShutdownWriteCommand)) {
++ ASSERT(!di->IsListeningSocket());
++ // Close the socket for writing.
++ VOID_NO_RETRY_EXPECTED(shutdown(di->fd(), SHUT_WR));
++ } else if (IS_COMMAND(msg[i].data, kCloseCommand)) {
++ // Close the socket and free system resources and move on to next
++ // message.
++ if (IS_SIGNAL_SOCKET(msg[i].data)) {
++ Process::ClearSignalHandlerByFd(di->fd(), socket->isolate_port());
++ }
++ intptr_t old_mask = di->Mask();
++ Dart_Port port = msg[i].dart_port;
++ if (port != ILLEGAL_PORT) {
++ di->RemovePort(port);
++ }
++ intptr_t new_mask = di->Mask();
++ UpdateEpollInstance(old_mask, di);
++
++ intptr_t fd = di->fd();
++ ASSERT(fd == socket->fd());
++ if (di->IsListeningSocket()) {
++ // We only close the socket file descriptor from the operating
++ // system if there are no other dart socket objects which
++ // are listening on the same (address, port) combination.
++ ListeningSocketRegistry* registry =
++ ListeningSocketRegistry::Instance();
++
++ MutexLocker locker(registry->mutex());
++
++ if (registry->CloseSafe(socket)) {
++ ASSERT(new_mask == 0);
++ socket_map_.Remove(GetHashmapKeyFromFd(fd),
++ GetHashmapHashFromFd(fd));
++ di->Close();
++ delete di;
++ }
++ socket->CloseFd();
++ } else {
++ ASSERT(new_mask == 0);
++ socket_map_.Remove(GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd));
++ di->Close();
++ delete di;
++ socket->CloseFd();
++ }
++ DartUtils::PostInt32(port, 1 << kDestroyedEvent);
++ } else if (IS_COMMAND(msg[i].data, kReturnTokenCommand)) {
++ int count = TOKEN_COUNT(msg[i].data);
++ intptr_t old_mask = di->Mask();
++ di->ReturnTokens(msg[i].dart_port, count);
++ UpdateEpollInstance(old_mask, di);
++ } else if (IS_COMMAND(msg[i].data, kSetEventMaskCommand)) {
++ // `events` can only have kInEvent/kOutEvent flags set.
++ intptr_t events = msg[i].data & EVENT_MASK;
++ ASSERT(0 == (events & ~(1 << kInEvent | 1 << kOutEvent)));
++
++ intptr_t old_mask = di->Mask();
++ di->SetPortAndMask(msg[i].dart_port, msg[i].data & EVENT_MASK);
++ UpdateEpollInstance(old_mask, di);
++ } else {
++ UNREACHABLE();
++ }
++ }
++ }
++}
++
++void EventHandlerImplementation::UpdateTimerFd() {
++ struct itimerspec it;
++ memset(&it, 0, sizeof(it));
++ if (timeout_queue_.HasTimeout()) {
++ int64_t millis = timeout_queue_.CurrentTimeout();
++ it.it_value.tv_sec = millis / 1000;
++ it.it_value.tv_nsec = (millis % 1000) * 1000000;
++ }
++ VOID_NO_RETRY_EXPECTED(
++ timerfd_settime(timer_fd_, TFD_TIMER_ABSTIME, &it, NULL));
++}
++
++#ifdef DEBUG_POLL
++static void PrintEventMask(intptr_t fd, intptr_t events) {
++ Syslog::Print("%d ", fd);
++ if ((events & EPOLLIN) != 0) {
++ Syslog::Print("EPOLLIN ");
++ }
++ if ((events & EPOLLPRI) != 0) {
++ Syslog::Print("EPOLLPRI ");
++ }
++ if ((events & EPOLLOUT) != 0) {
++ Syslog::Print("EPOLLOUT ");
++ }
++ if ((events & EPOLLERR) != 0) {
++ Syslog::Print("EPOLLERR ");
++ }
++ if ((events & EPOLLHUP) != 0) {
++ Syslog::Print("EPOLLHUP ");
++ }
++ if ((events & EPOLLRDHUP) != 0) {
++ Syslog::Print("EPOLLRDHUP ");
++ }
++ int all_events =
++ EPOLLIN | EPOLLPRI | EPOLLOUT | EPOLLERR | EPOLLHUP | EPOLLRDHUP;
++ if ((events & ~all_events) != 0) {
++ Syslog::Print("(and %08x) ", events & ~all_events);
++ }
++ Syslog::Print("(available %d) ", FDUtils::AvailableBytes(fd));
++
++ Syslog::Print("\n");
++}
++#endif
++
++intptr_t EventHandlerImplementation::GetPollEvents(intptr_t events,
++ DescriptorInfo* di) {
++#ifdef DEBUG_POLL
++ PrintEventMask(di->fd(), events);
++#endif
++ if ((events & EPOLLERR) != 0) {
++ // Return error only if EPOLLIN is present.
++ return ((events & EPOLLIN) != 0) ? (1 << kErrorEvent) : 0;
++ }
++ intptr_t event_mask = 0;
++ if ((events & EPOLLIN) != 0) {
++ event_mask |= (1 << kInEvent);
++ }
++ if ((events & EPOLLOUT) != 0) {
++ event_mask |= (1 << kOutEvent);
++ }
++ if ((events & (EPOLLHUP | EPOLLRDHUP)) != 0) {
++ event_mask |= (1 << kCloseEvent);
++ }
++ return event_mask;
++}
++
++void EventHandlerImplementation::HandleEvents(struct epoll_event* events,
++ int size) {
++ bool interrupt_seen = false;
++ for (int i = 0; i < size; i++) {
++ if (events[i].data.ptr == NULL) {
++ interrupt_seen = true;
++ } else if (events[i].data.fd == timer_fd_) {
++ int64_t val;
++ VOID_TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER(
++ read(timer_fd_, &val, sizeof(val)));
++ if (timeout_queue_.HasTimeout()) {
++ DartUtils::PostNull(timeout_queue_.CurrentPort());
++ timeout_queue_.RemoveCurrent();
++ }
++ UpdateTimerFd();
++ } else {
++ DescriptorInfo* di =
++ reinterpret_cast(events[i].data.ptr);
++ const intptr_t old_mask = di->Mask();
++ const intptr_t event_mask = GetPollEvents(events[i].events, di);
++ if ((event_mask & (1 << kErrorEvent)) != 0) {
++ di->NotifyAllDartPorts(event_mask);
++ UpdateEpollInstance(old_mask, di);
++ } else if (event_mask != 0) {
++ Dart_Port port = di->NextNotifyDartPort(event_mask);
++ ASSERT(port != 0);
++ UpdateEpollInstance(old_mask, di);
++ DartUtils::PostInt32(port, event_mask);
++ }
++ }
++ }
++ if (interrupt_seen) {
++ // Handle after socket events, so we avoid closing a socket before we handle
++ // the current events.
++ HandleInterruptFd();
++ }
++}
++
++void EventHandlerImplementation::Poll(uword args) {
++ ThreadSignalBlocker signal_blocker(SIGPROF);
++ static const intptr_t kMaxEvents = 16;
++ struct epoll_event events[kMaxEvents];
++ EventHandler* handler = reinterpret_cast(args);
++ EventHandlerImplementation* handler_impl = &handler->delegate_;
++ ASSERT(handler_impl != NULL);
++
++ while (!handler_impl->shutdown_) {
++ intptr_t result = TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER(
++ epoll_wait(handler_impl->epoll_fd_, events, kMaxEvents, -1));
++ ASSERT(EAGAIN == EWOULDBLOCK);
++ if (result <= 0) {
++ if (errno != EWOULDBLOCK) {
++ perror("Poll failed");
++ }
++ } else {
++ handler_impl->HandleEvents(events, result);
++ }
++ }
++ DEBUG_ASSERT(ReferenceCounted::instances() == 0);
++ handler->NotifyShutdownDone();
++}
++
++void EventHandlerImplementation::Start(EventHandler* handler) {
++ int result =
++ Thread::Start("dart:io EventHandler", &EventHandlerImplementation::Poll,
++ reinterpret_cast(handler));
++ if (result != 0) {
++ FATAL("Failed to start event handler thread %d", result);
++ }
++}
++
++void EventHandlerImplementation::Shutdown() {
++ SendData(kShutdownId, 0, 0);
++}
++
++void EventHandlerImplementation::SendData(intptr_t id,
++ Dart_Port dart_port,
++ int64_t data) {
++ WakeupHandler(id, dart_port, data);
++}
++
++void* EventHandlerImplementation::GetHashmapKeyFromFd(intptr_t fd) {
++ // The hashmap does not support keys with value 0.
++ return reinterpret_cast(fd + 1);
++}
++
++uint32_t EventHandlerImplementation::GetHashmapHashFromFd(intptr_t fd) {
++ // The hashmap does not support keys with value 0.
++ return dart::Utils::WordHash(fd + 1);
++}
++
++} // namespace bin
++} // namespace dart
++
++#endif // defined(DART_HOST_OS_OHOS)
+diff --git a/runtime/bin/eventhandler_ohos.h b/runtime/bin/eventhandler_ohos.h
+new file mode 100644
+index 00000000000..0e4d84c8c12
+--- /dev/null
++++ b/runtime/bin/eventhandler_ohos.h
+@@ -0,0 +1,99 @@
++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
++// for details. All rights reserved. Use of this source code is governed by a
++// BSD-style license that can be found in the LICENSE file.
++
++#ifndef RUNTIME_BIN_EVENTHANDLER_OHOS_H_
++#define RUNTIME_BIN_EVENTHANDLER_OHOS_H_
++
++#if !defined(RUNTIME_BIN_EVENTHANDLER_H_)
++#error Do not include eventhandler_linux.h directly; use eventhandler.h instead.
++#endif
++
++#include
++#include
++#include
++#include
++
++#include "platform/hashmap.h"
++#include "platform/signal_blocker.h"
++
++namespace dart {
++namespace bin {
++
++class DescriptorInfo : public DescriptorInfoBase {
++ public:
++ explicit DescriptorInfo(intptr_t fd) : DescriptorInfoBase(fd) {}
++
++ virtual ~DescriptorInfo() {}
++
++ intptr_t GetPollEvents();
++
++ virtual void Close() {
++ close(fd_);
++ fd_ = -1;
++ }
++
++ private:
++ DISALLOW_COPY_AND_ASSIGN(DescriptorInfo);
++};
++
++class DescriptorInfoSingle : public DescriptorInfoSingleMixin {
++ public:
++ explicit DescriptorInfoSingle(intptr_t fd)
++ : DescriptorInfoSingleMixin(fd, false) {}
++ virtual ~DescriptorInfoSingle() {}
++
++ private:
++ DISALLOW_COPY_AND_ASSIGN(DescriptorInfoSingle);
++};
++
++class DescriptorInfoMultiple
++ : public DescriptorInfoMultipleMixin {
++ public:
++ explicit DescriptorInfoMultiple(intptr_t fd)
++ : DescriptorInfoMultipleMixin(fd, false) {}
++ virtual ~DescriptorInfoMultiple() {}
++
++ private:
++ DISALLOW_COPY_AND_ASSIGN(DescriptorInfoMultiple);
++};
++
++class EventHandlerImplementation {
++ public:
++ EventHandlerImplementation();
++ ~EventHandlerImplementation();
++
++ void UpdateEpollInstance(intptr_t old_mask, DescriptorInfo* di);
++
++ // Gets the socket data structure for a given file
++ // descriptor. Creates a new one if one is not found.
++ DescriptorInfo* GetDescriptorInfo(intptr_t fd, bool is_listening);
++ void SendData(intptr_t id, Dart_Port dart_port, int64_t data);
++ void Start(EventHandler* handler);
++ void Shutdown();
++
++ private:
++ void HandleEvents(struct epoll_event* events, int size);
++ static void Poll(uword args);
++ void WakeupHandler(intptr_t id, Dart_Port dart_port, int64_t data);
++ void HandleInterruptFd();
++ void UpdateTimerFd();
++ void SetPort(intptr_t fd, Dart_Port dart_port, intptr_t mask);
++ intptr_t GetPollEvents(intptr_t events, DescriptorInfo* di);
++ static void* GetHashmapKeyFromFd(intptr_t fd);
++ static uint32_t GetHashmapHashFromFd(intptr_t fd);
++
++ SimpleHashMap socket_map_;
++ TimeoutQueue timeout_queue_;
++ bool shutdown_;
++ int interrupt_fds_[2];
++ int epoll_fd_;
++ int timer_fd_;
++
++ DISALLOW_COPY_AND_ASSIGN(EventHandlerImplementation);
++};
++
++} // namespace bin
++} // namespace dart
++
++#endif // RUNTIME_BIN_EVENTHANDLER_OHOS_H_
+diff --git a/runtime/bin/fdutils_ohos.cc b/runtime/bin/fdutils_ohos.cc
+new file mode 100644
+index 00000000000..f7531006cbe
+--- /dev/null
++++ b/runtime/bin/fdutils_ohos.cc
+@@ -0,0 +1,142 @@
++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
++// for details. All rights reserved. Use of this source code is governed by a
++// BSD-style license that can be found in the LICENSE file.
++
++#include "platform/globals.h"
++#if defined(DART_HOST_OS_OHOS)
++
++#include "bin/fdutils.h"
++
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++
++#include "platform/signal_blocker.h"
++
++namespace dart {
++namespace bin {
++
++bool FDUtils::SetCloseOnExec(intptr_t fd) {
++ intptr_t status;
++ status = NO_RETRY_EXPECTED(fcntl(fd, F_GETFD));
++ if (status < 0) {
++ perror("fcntl(F_GETFD) failed");
++ return false;
++ }
++ status |= FD_CLOEXEC;
++ if (NO_RETRY_EXPECTED(fcntl(fd, F_SETFD, status)) < 0) {
++ perror("fcntl(F_SETFD, FD_CLOEXEC) failed");
++ return false;
++ }
++ return true;
++}
++
++static bool SetBlockingHelper(intptr_t fd, bool blocking) {
++ intptr_t status;
++ status = NO_RETRY_EXPECTED(fcntl(fd, F_GETFL));
++ if (status < 0) {
++ perror("fcntl(F_GETFL) failed");
++ return false;
++ }
++ status = blocking ? (status & ~O_NONBLOCK) : (status | O_NONBLOCK);
++ if (NO_RETRY_EXPECTED(fcntl(fd, F_SETFL, status)) < 0) {
++ perror("fcntl(F_SETFL, O_NONBLOCK) failed");
++ return false;
++ }
++ return true;
++}
++
++bool FDUtils::SetNonBlocking(intptr_t fd) {
++ return SetBlockingHelper(fd, false);
++}
++
++bool FDUtils::SetBlocking(intptr_t fd) {
++ return SetBlockingHelper(fd, true);
++}
++
++bool FDUtils::IsBlocking(intptr_t fd, bool* is_blocking) {
++ intptr_t status;
++ status = NO_RETRY_EXPECTED(fcntl(fd, F_GETFL));
++ if (status < 0) {
++ return false;
++ }
++ *is_blocking = (status & O_NONBLOCK) == 0;
++ return true;
++}
++
++intptr_t FDUtils::AvailableBytes(intptr_t fd) {
++ int available; // ioctl for FIONREAD expects an 'int*' argument.
++ int result = NO_RETRY_EXPECTED(ioctl(fd, FIONREAD, &available));
++ if (result < 0) {
++ return result;
++ }
++ ASSERT(available >= 0);
++ return static_cast(available);
++}
++
++ssize_t FDUtils::ReadFromBlocking(int fd, void* buffer, size_t count) {
++#ifdef DEBUG
++ bool is_blocking = false;
++ ASSERT(FDUtils::IsBlocking(fd, &is_blocking));
++ ASSERT(is_blocking);
++#endif
++ size_t remaining = count;
++ char* buffer_pos = reinterpret_cast(buffer);
++ while (remaining > 0) {
++ ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer_pos, remaining));
++ if (bytes_read == 0) {
++ return count - remaining;
++ } else if (bytes_read == -1) {
++ ASSERT(EAGAIN == EWOULDBLOCK);
++ // Error code EWOULDBLOCK should only happen for non blocking
++ // file descriptors.
++ ASSERT(errno != EWOULDBLOCK);
++ return -1;
++ } else {
++ ASSERT(bytes_read > 0);
++ remaining -= bytes_read;
++ buffer_pos += bytes_read;
++ }
++ }
++ return count;
++}
++
++ssize_t FDUtils::WriteToBlocking(int fd, const void* buffer, size_t count) {
++#ifdef DEBUG
++ bool is_blocking = false;
++ ASSERT(FDUtils::IsBlocking(fd, &is_blocking));
++ ASSERT(is_blocking);
++#endif
++ size_t remaining = count;
++ char* buffer_pos = const_cast(reinterpret_cast(buffer));
++ while (remaining > 0) {
++ ssize_t bytes_written =
++ TEMP_FAILURE_RETRY(write(fd, buffer_pos, remaining));
++ if (bytes_written == 0) {
++ return count - remaining;
++ } else if (bytes_written == -1) {
++ ASSERT(EAGAIN == EWOULDBLOCK);
++ // Error code EWOULDBLOCK should only happen for non blocking
++ // file descriptors.
++ ASSERT(errno != EWOULDBLOCK);
++ return -1;
++ } else {
++ ASSERT(bytes_written > 0);
++ remaining -= bytes_written;
++ buffer_pos += bytes_written;
++ }
++ }
++ return count;
++}
++
++void FDUtils::SaveErrorAndClose(intptr_t fd) {
++ int err = errno;
++ close(fd);
++ errno = err;
++}
++
++} // namespace bin
++} // namespace dart
++
++#endif // defined(DART_HOST_OS_OHOS)
+diff --git a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
+index a66574162f2..ff34aa04738 100644
+--- a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
++++ b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
+@@ -1295,7 +1295,7 @@ DART_EXPORT void WaitUntilNThreadsEnterBarrier(intptr_t num_threads) {
+ ////////////////////////////////////////////////////////////////////////////////
+
+ #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || \
+- defined(DART_HOST_OS_MACOS)
++ defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS)
+ static bool Regress216834909_hang_at_exit = true;
+
+ static void Regress216834909_AtExit() {
+@@ -1317,7 +1317,7 @@ DART_EXPORT void Regress216834909_SetAtExit(int64_t install) {
+ }
+ }
+ #endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ||
+- // defined(DART_HOST_OS_MACOS)
++ // defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS)
+
+ DART_EXPORT bool IsNull(Dart_Handle object) {
+ return Dart_IsNull(object);
+diff --git a/runtime/bin/ffi_unit_test/BUILD.gn b/runtime/bin/ffi_unit_test/BUILD.gn
+index 430b908d86b..b3c1f15b9b9 100644
+--- a/runtime/bin/ffi_unit_test/BUILD.gn
++++ b/runtime/bin/ffi_unit_test/BUILD.gn
+@@ -73,6 +73,10 @@ config("define_target_os_linux") {
+ defines = [ "DART_TARGET_OS_LINUX" ]
+ }
+
++config("define_target_os_ohos") {
++ defines = [ "DART_TARGET_OS_OHOS" ]
++}
++
+ config("define_target_os_macos") {
+ defines = [ "DART_TARGET_OS_MACOS" ]
+ }
+diff --git a/runtime/bin/file_ohos.cc b/runtime/bin/file_ohos.cc
+new file mode 100644
+index 00000000000..8ac60ad5e64
+--- /dev/null
++++ b/runtime/bin/file_ohos.cc
+@@ -0,0 +1,787 @@
++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
++// for details. All rights reserved. Use of this source code is governed by a
++// BSD-style license that can be found in the LICENSE file.
++
++#include "platform/globals.h"
++#if defined(DART_HOST_OS_OHOS)
++
++#include "bin/file.h"
++
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++
++#include "bin/builtin.h"
++#include "bin/fdutils.h"
++#include "bin/namespace.h"
++#include "platform/signal_blocker.h"
++#include "platform/syslog.h"
++#include "platform/utils.h"
++
++namespace dart {
++namespace bin {
++
++class FileHandle {
++ public:
++ explicit FileHandle(int fd) : fd_(fd) {}
++ ~FileHandle() {}
++ int fd() const { return fd_; }
++ void set_fd(int fd) { fd_ = fd; }
++
++ private:
++ int fd_;
++
++ DISALLOW_COPY_AND_ASSIGN(FileHandle);
++};
++
++File::~File() {
++ if (!IsClosed() && (handle_->fd() != STDOUT_FILENO) &&
++ (handle_->fd() != STDERR_FILENO)) {
++ Close();
++ }
++ delete handle_;
++}
++
++void File::Close() {
++ ASSERT(handle_->fd() >= 0);
++ if (handle_->fd() == STDOUT_FILENO) {
++ // If stdout, redirect fd to /dev/null.
++ int null_fd = TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY));
++ ASSERT(null_fd >= 0);
++ VOID_TEMP_FAILURE_RETRY(dup2(null_fd, handle_->fd()));
++ close(null_fd);
++ } else {
++ int err = close(handle_->fd());
++ if (err != 0) {
++ const int kBufferSize = 1024;
++ char error_buf[kBufferSize];
++ Syslog::PrintErr("%s\n", Utils::StrError(errno, error_buf, kBufferSize));
++ }
++ }
++ handle_->set_fd(kClosedFd);
++}
++
++intptr_t File::GetFD() {
++ return handle_->fd();
++}
++
++bool File::IsClosed() {
++ return handle_->fd() == kClosedFd;
++}
++
++MappedMemory* File::Map(MapType type,
++ int64_t position,
++ int64_t length,
++ void* start) {
++ ASSERT(handle_->fd() >= 0);
++ ASSERT(length > 0);
++ void* hint = nullptr;
++ int prot = PROT_NONE;
++ int flags = MAP_PRIVATE;
++ switch (type) {
++ case kReadOnly:
++ prot = PROT_READ;
++ break;
++ case kReadExecute:
++ // Try to allocate near the VM's binary.
++ hint = reinterpret_cast(&Dart_Initialize);
++ prot = PROT_READ | PROT_EXEC;
++ flags |= (MAP_JIT_OHOS | MAP_ANONYMOUS);
++ break;
++ case kReadWrite:
++ prot = PROT_READ | PROT_WRITE;
++ break;
++ }
++ if (start != nullptr) {
++ hint = start;
++ flags |= MAP_FIXED;
++ }
++ void* addr = mmap(hint, length, prot, flags, handle_->fd(), position);
++ if (addr == MAP_FAILED) {
++ return NULL;
++ }
++ return new MappedMemory(addr, length, /*should_unmap=*/start == nullptr);
++}
++
++void MappedMemory::Unmap() {
++ int result = munmap(address_, size_);
++ ASSERT(result == 0);
++ address_ = 0;
++ size_ = 0;
++}
++
++int64_t File::Read(void* buffer, int64_t num_bytes) {
++ ASSERT(handle_->fd() >= 0);
++ return TEMP_FAILURE_RETRY(read(handle_->fd(), buffer, num_bytes));
++}
++
++int64_t File::Write(const void* buffer, int64_t num_bytes) {
++ ASSERT(handle_->fd() >= 0);
++ return TEMP_FAILURE_RETRY(write(handle_->fd(), buffer, num_bytes));
++}
++
++bool File::VPrint(const char* format, va_list args) {
++ // Measure.
++ va_list measure_args;
++ va_copy(measure_args, args);
++ intptr_t len = vsnprintf(NULL, 0, format, measure_args);
++ va_end(measure_args);
++
++ char* buffer = reinterpret_cast(malloc(len + 1));
++
++ // Print.
++ va_list print_args;
++ va_copy(print_args, args);
++ vsnprintf(buffer, len + 1, format, print_args);
++ va_end(print_args);
++
++ bool result = WriteFully(buffer, len);
++ free(buffer);
++ return result;
++}
++
++int64_t File::Position() {
++ ASSERT(handle_->fd() >= 0);
++ return NO_RETRY_EXPECTED(lseek64(handle_->fd(), 0, SEEK_CUR));
++}
++
++bool File::SetPosition(int64_t position) {
++ ASSERT(handle_->fd() >= 0);
++ return NO_RETRY_EXPECTED(lseek64(handle_->fd(), position, SEEK_SET)) >= 0;
++}
++
++bool File::Truncate(int64_t length) {
++ ASSERT(handle_->fd() >= 0);
++ return TEMP_FAILURE_RETRY(ftruncate64(handle_->fd(), length) != -1);
++}
++
++bool File::Flush() {
++ ASSERT(handle_->fd() >= 0);
++ return NO_RETRY_EXPECTED(fsync(handle_->fd())) != -1;
++}
++
++bool File::Lock(File::LockType lock, int64_t start, int64_t end) {
++ ASSERT(handle_->fd() >= 0);
++ ASSERT((end == -1) || (end > start));
++ struct flock fl;
++ switch (lock) {
++ case File::kLockUnlock:
++ fl.l_type = F_UNLCK;
++ break;
++ case File::kLockShared:
++ case File::kLockBlockingShared:
++ fl.l_type = F_RDLCK;
++ break;
++ case File::kLockExclusive:
++ case File::kLockBlockingExclusive:
++ fl.l_type = F_WRLCK;
++ break;
++ default:
++ return false;
++ }
++ fl.l_whence = SEEK_SET;
++ fl.l_start = start;
++ fl.l_len = end == -1 ? 0 : end - start;
++ int cmd = F_SETLK;
++ if ((lock == File::kLockBlockingShared) ||
++ (lock == File::kLockBlockingExclusive)) {
++ cmd = F_SETLKW;
++ }
++ return TEMP_FAILURE_RETRY(fcntl(handle_->fd(), cmd, &fl)) != -1;
++}
++
++int64_t File::Length() {
++ ASSERT(handle_->fd() >= 0);
++ struct stat64 st;
++ if (TEMP_FAILURE_RETRY(fstat64(handle_->fd(), &st)) == 0) {
++ return st.st_size;
++ }
++ return -1;
++}
++
++File* File::FileOpenW(const wchar_t* system_name, FileOpenMode mode) {
++ UNREACHABLE();
++ return NULL;
++}
++
++File* File::OpenFD(int fd) {
++ return new File(new FileHandle(fd));
++}
++
++File* File::Open(Namespace* namespc, const char* name, FileOpenMode mode) {
++ NamespaceScope ns(namespc, name);
++ // Report errors for non-regular files.
++ struct stat64 st;
++ if (TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &st, 0)) == 0) {
++ // Only accept regular files, character devices, and pipes.
++ if (!S_ISREG(st.st_mode) && !S_ISCHR(st.st_mode) && !S_ISFIFO(st.st_mode)) {
++ errno = (S_ISDIR(st.st_mode)) ? EISDIR : ENOENT;
++ return NULL;
++ }
++ }
++ int flags = O_RDONLY;
++ if ((mode & kWrite) != 0) {
++ ASSERT((mode & kWriteOnly) == 0);
++ flags = (O_RDWR | O_CREAT);
++ }
++ if ((mode & kWriteOnly) != 0) {
++ ASSERT((mode & kWrite) == 0);
++ flags = (O_WRONLY | O_CREAT);
++ }
++ if ((mode & kTruncate) != 0) {
++ flags = flags | O_TRUNC;
++ }
++ flags |= O_CLOEXEC;
++ const int fd = TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), flags, 0666));
++ if (fd < 0) {
++ return NULL;
++ }
++ if ((((mode & kWrite) != 0) && ((mode & kTruncate) == 0)) ||
++ (((mode & kWriteOnly) != 0) && ((mode & kTruncate) == 0))) {
++ int64_t position = NO_RETRY_EXPECTED(lseek64(fd, 0, SEEK_END));
++ if (position < 0) {
++ return NULL;
++ }
++ }
++ return OpenFD(fd);
++}
++
++Utils::CStringUniquePtr File::UriToPath(const char* uri) {
++ const char* path = (strlen(uri) >= 8 && strncmp(uri, "file:///", 8) == 0)
++ ? uri + 7 : uri;
++ UriDecoder uri_decoder(path);
++ if (uri_decoder.decoded() == nullptr) {
++ errno = EINVAL;
++ return Utils::CreateCStringUniquePtr(nullptr);
++ }
++ return Utils::CreateCStringUniquePtr(strdup(uri_decoder.decoded()));
++}
++
++File* File::OpenUri(Namespace* namespc, const char* uri, FileOpenMode mode) {
++ auto path = UriToPath(uri);
++ if (path == nullptr) {
++ return nullptr;
++ }
++ return File::Open(namespc, path.get(), mode);
++}
++
++File* File::OpenStdio(int fd) {
++ return new File(new FileHandle(fd));
++}
++
++bool File::Exists(Namespace* namespc, const char* name) {
++ NamespaceScope ns(namespc, name);
++ struct stat64 st;
++ if (TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &st, 0)) == 0) {
++ // Everything but a directory and a link is a file to Dart.
++ return !S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode);
++ } else {
++ return false;
++ }
++}
++
++bool File::ExistsUri(Namespace* namespc, const char* uri) {
++ auto path = UriToPath(uri);
++ if (path == nullptr) {
++ return false;
++ }
++ return File::Exists(namespc, path.get());
++}
++
++bool File::Create(Namespace* namespc, const char* name, bool exclusive) {
++ NamespaceScope ns(namespc, name);
++ int flags = O_RDONLY | O_CREAT | O_CLOEXEC;
++ if (exclusive) {
++ flags |= O_EXCL;
++ }
++ const int fd = TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), flags, 0666));
++ if (fd < 0) {
++ return false;
++ }
++ // File.create returns a File, so we shouldn't be giving the illusion that the
++ // call has created a file or that a file already exists if there is already
++ // an entity at the same path that is a directory or a link.
++ bool is_file = true;
++ struct stat64 st;
++ if (TEMP_FAILURE_RETRY(fstat64(fd, &st)) == 0) {
++ if (S_ISDIR(st.st_mode)) {
++ errno = EISDIR;
++ is_file = false;
++ } else if (S_ISLNK(st.st_mode)) {
++ errno = ENOENT;
++ is_file = false;
++ }
++ }
++ FDUtils::SaveErrorAndClose(fd);
++ return is_file;
++}
++
++bool File::CreateLink(Namespace* namespc,
++ const char* name,
++ const char* target) {
++ NamespaceScope ns(namespc, name);
++ return NO_RETRY_EXPECTED(symlinkat(target, ns.fd(), ns.path())) == 0;
++}
++
++bool File::CreatePipe(Namespace* namespc, File** readPipe, File** writePipe) {
++ int pipe_fds[2];
++ int status = NO_RETRY_EXPECTED(pipe(pipe_fds));
++ if (status != 0) {
++ return false;
++ }
++ *readPipe = OpenFD(pipe_fds[0]);
++ *writePipe = OpenFD(pipe_fds[1]);
++ return true;
++}
++
++File::Type File::GetType(Namespace* namespc,
++ const char* name,
++ bool follow_links) {
++ NamespaceScope ns(namespc, name);
++ struct stat64 entry_info;
++ int stat_success;
++ if (follow_links) {
++ stat_success =
++ TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &entry_info, 0));
++ } else {
++ stat_success = TEMP_FAILURE_RETRY(
++ fstatat64(ns.fd(), ns.path(), &entry_info, AT_SYMLINK_NOFOLLOW));
++ }
++ if (stat_success == -1) {
++ return File::kDoesNotExist;
++ }
++ if (S_ISDIR(entry_info.st_mode)) {
++ return File::kIsDirectory;
++ }
++ if (S_ISREG(entry_info.st_mode)) {
++ return File::kIsFile;
++ }
++ if (S_ISLNK(entry_info.st_mode)) {
++ return File::kIsLink;
++ }
++ if (S_ISSOCK(entry_info.st_mode)) {
++ return File::kIsSock;
++ }
++ if (S_ISFIFO(entry_info.st_mode)) {
++ return File::kIsPipe;
++ }
++ return File::kDoesNotExist;
++}
++
++static void SetErrno(File::Type type) {
++ switch (type) {
++ case File::kIsDirectory:
++ errno = EISDIR;
++ break;
++ case File::kDoesNotExist:
++ errno = ENOENT;
++ break;
++ default:
++ errno = EINVAL;
++ break;
++ }
++}
++
++static bool CheckTypeAndSetErrno(Namespace* namespc,
++ const char* name,
++ File::Type expected,
++ bool follow_links) {
++ File::Type actual = File::GetType(namespc, name, follow_links);
++ if (actual == expected) {
++ return true;
++ }
++ SetErrno(actual);
++ return false;
++}
++
++bool File::Delete(Namespace* namespc, const char* name) {
++ File::Type type = File::GetType(namespc, name, true);
++ if (type == kIsFile || type == kIsSock || type == kIsPipe) {
++ NamespaceScope ns(namespc, name);
++ return (NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), 0)) == 0);
++ }
++ SetErrno(type);
++ return false;
++}
++
++bool File::DeleteLink(Namespace* namespc, const char* name) {
++ NamespaceScope ns(namespc, name);
++ return CheckTypeAndSetErrno(namespc, name, kIsLink, false) &&
++ (NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), 0)) == 0);
++}
++
++bool File::Rename(Namespace* namespc,
++ const char* old_path,
++ const char* new_path) {
++ File::Type type = File::GetType(namespc, old_path, true);
++ if (type == kIsFile || type == kIsSock || type == kIsPipe) {
++ NamespaceScope oldns(namespc, old_path);
++ NamespaceScope newns(namespc, new_path);
++ return (NO_RETRY_EXPECTED(renameat(oldns.fd(), oldns.path(), newns.fd(),
++ newns.path())) == 0);
++ }
++ SetErrno(type);
++ return false;
++}
++
++bool File::RenameLink(Namespace* namespc,
++ const char* old_path,
++ const char* new_path) {
++ NamespaceScope oldns(namespc, old_path);
++ NamespaceScope newns(namespc, new_path);
++ return CheckTypeAndSetErrno(namespc, old_path, kIsLink, false) &&
++ (NO_RETRY_EXPECTED(renameat(oldns.fd(), oldns.path(), newns.fd(),
++ newns.path())) == 0);
++}
++
++bool File::Copy(Namespace* namespc,
++ const char* old_path,
++ const char* new_path) {
++ File::Type type = File::GetType(namespc, old_path, true);
++ if (type != kIsFile && type != kIsSock && type != kIsPipe) {
++ SetErrno(type);
++ return false;
++ }
++ NamespaceScope oldns(namespc, old_path);
++ struct stat64 st;
++ if (TEMP_FAILURE_RETRY(fstatat64(oldns.fd(), oldns.path(), &st, 0)) != 0) {
++ return false;
++ }
++ const int old_fd = TEMP_FAILURE_RETRY(
++ openat64(oldns.fd(), oldns.path(), O_RDONLY | O_CLOEXEC));
++ if (old_fd < 0) {
++ return false;
++ }
++ NamespaceScope newns(namespc, new_path);
++ const int new_fd = TEMP_FAILURE_RETRY(
++ openat64(newns.fd(), newns.path(),
++ O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, st.st_mode));
++ if (new_fd < 0) {
++ close(old_fd);
++ return false;
++ }
++ int64_t offset = 0;
++ intptr_t result = 1;
++ while (result > 0) {
++ // Loop to ensure we copy everything, and not only up to 2GB.
++ result = NO_RETRY_EXPECTED(sendfile64(new_fd, old_fd, &offset, kMaxUint32));
++ }
++ // From sendfile man pages:
++ // Applications may wish to fall back to read(2)/write(2) in the case
++ // where sendfile() fails with EINVAL or ENOSYS.
++ if ((result < 0) && ((errno == EINVAL) || (errno == ENOSYS))) {
++ const intptr_t kBufferSize = 8 * KB;
++ uint8_t* buffer = reinterpret_cast(malloc(kBufferSize));
++ while ((result = TEMP_FAILURE_RETRY(read(old_fd, buffer, kBufferSize))) >
++ 0) {
++ int wrote = TEMP_FAILURE_RETRY(write(new_fd, buffer, result));
++ if (wrote != result) {
++ result = -1;
++ break;
++ }
++ }
++ free(buffer);
++ }
++ int e = errno;
++ close(old_fd);
++ close(new_fd);
++ if (result < 0) {
++ VOID_NO_RETRY_EXPECTED(unlinkat(newns.fd(), newns.path(), 0));
++ errno = e;
++ return false;
++ }
++ return true;
++}
++
++static bool StatHelper(Namespace* namespc,
++ const char* name,
++ struct stat64* st) {
++ NamespaceScope ns(namespc, name);
++ if (TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), st, 0)) != 0) {
++ return false;
++ }
++ // Signal an error if it's a directory.
++ if (S_ISDIR(st->st_mode)) {
++ errno = EISDIR;
++ return false;
++ }
++ // Otherwise assume the caller knows what it's doing.
++ return true;
++}
++
++int64_t File::LengthFromPath(Namespace* namespc, const char* name) {
++ struct stat64 st;
++ if (!StatHelper(namespc, name, &st)) {
++ return -1;
++ }
++ return st.st_size;
++}
++
++static int64_t TimespecToMilliseconds(const struct timespec& t) {
++ return static_cast(t.tv_sec) * 1000L +
++ static_cast(t.tv_nsec) / 1000000L;
++}
++
++static void MillisecondsToTimespec(int64_t millis, struct timespec* t) {
++ ASSERT(t != NULL);
++ t->tv_sec = millis / kMillisecondsPerSecond;
++ t->tv_nsec = (millis % kMillisecondsPerSecond) * 1000L;
++}
++
++void File::Stat(Namespace* namespc, const char* name, int64_t* data) {
++ NamespaceScope ns(namespc, name);
++ struct stat64 st;
++ if (TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &st, 0)) == 0) {
++ if (S_ISREG(st.st_mode)) {
++ data[kType] = kIsFile;
++ } else if (S_ISDIR(st.st_mode)) {
++ data[kType] = kIsDirectory;
++ } else if (S_ISLNK(st.st_mode)) {
++ data[kType] = kIsLink;
++ } else if (S_ISSOCK(st.st_mode)) {
++ data[kType] = kIsSock;
++ } else if (S_ISFIFO(st.st_mode)) {
++ data[kType] = kIsPipe;
++ } else {
++ data[kType] = kDoesNotExist;
++ }
++ data[kCreatedTime] = TimespecToMilliseconds(st.st_ctim);
++ data[kModifiedTime] = TimespecToMilliseconds(st.st_mtim);
++ data[kAccessedTime] = TimespecToMilliseconds(st.st_atim);
++ data[kMode] = st.st_mode;
++ data[kSize] = st.st_size;
++ } else {
++ data[kType] = kDoesNotExist;
++ }
++}
++
++time_t File::LastModified(Namespace* namespc, const char* name) {
++ struct stat64 st;
++ if (!StatHelper(namespc, name, &st)) {
++ return -1;
++ }
++ return st.st_mtime;
++}
++
++time_t File::LastAccessed(Namespace* namespc, const char* name) {
++ struct stat64 st;
++ if (!StatHelper(namespc, name, &st)) {
++ return -1;
++ }
++ return st.st_atime;
++}
++
++bool File::SetLastAccessed(Namespace* namespc,
++ const char* name,
++ int64_t millis) {
++ // First get the current times.
++ struct stat64 st;
++ if (!StatHelper(namespc, name, &st)) {
++ return false;
++ }
++
++ // Set the new time:
++ NamespaceScope ns(namespc, name);
++ struct timespec times[2];
++ MillisecondsToTimespec(millis, ×[0]);
++ times[1] = st.st_mtim;
++ return utimensat(ns.fd(), ns.path(), times, 0) == 0;
++}
++
++bool File::SetLastModified(Namespace* namespc,
++ const char* name,
++ int64_t millis) {
++ // First get the current times.
++ struct stat64 st;
++ if (!StatHelper(namespc, name, &st)) {
++ return false;
++ }
++
++ // Set the new time:
++ NamespaceScope ns(namespc, name);
++ struct timespec times[2];
++ times[0] = st.st_atim;
++ MillisecondsToTimespec(millis, ×[1]);
++ return utimensat(ns.fd(), ns.path(), times, 0) == 0;
++}
++
++const char* File::LinkTarget(Namespace* namespc,
++ const char* name,
++ char* dest,
++ int dest_size) {
++ NamespaceScope ns(namespc, name);
++ struct stat64 link_stats;
++ const int status = TEMP_FAILURE_RETRY(
++ fstatat64(ns.fd(), ns.path(), &link_stats, AT_SYMLINK_NOFOLLOW));
++ if (status != 0) {
++ return NULL;
++ }
++ if (!S_ISLNK(link_stats.st_mode)) {
++ errno = ENOENT;
++ return NULL;
++ }
++ // Don't rely on the link_stats.st_size for the size of the link
++ // target. For some filesystems, e.g. procfs, this value is always
++ // 0. Also the link might have changed before the readlink call.
++ const int kBufferSize = PATH_MAX + 1;
++ char target[kBufferSize];
++ const int target_size =
++ TEMP_FAILURE_RETRY(readlinkat(ns.fd(), ns.path(), target, kBufferSize));
++ if (target_size <= 0) {
++ return NULL;
++ }
++ if (dest == NULL) {
++ dest = DartUtils::ScopedCString(target_size + 1);
++ } else {
++ ASSERT(dest_size > 0);
++ if (dest_size <= target_size) {
++ return NULL;
++ }
++ }
++ memmove(dest, target, target_size);
++ dest[target_size] = '\0';
++ return dest;
++}
++
++bool File::IsAbsolutePath(const char* pathname) {
++ return (pathname != NULL) && (pathname[0] == '/');
++}
++
++intptr_t File::ReadLinkInto(const char* pathname,
++ char* result,
++ size_t result_size) {
++ ASSERT(pathname != NULL);
++ ASSERT(IsAbsolutePath(pathname));
++ struct stat64 link_stats;
++ if (TEMP_FAILURE_RETRY(lstat64(pathname, &link_stats)) != 0) {
++ return -1;
++ }
++ if (!S_ISLNK(link_stats.st_mode)) {
++ errno = ENOENT;
++ return -1;
++ }
++ size_t target_size =
++ TEMP_FAILURE_RETRY(readlink(pathname, result, result_size));
++ if (target_size <= 0) {
++ return -1;
++ }
++ // readlink returns non-zero terminated strings. Append.
++ if (target_size < result_size) {
++ result[target_size] = '\0';
++ target_size++;
++ }
++ return target_size;
++}
++
++const char* File::ReadLink(const char* pathname) {
++ // Don't rely on the link_stats.st_size for the size of the link
++ // target. For some filesystems, e.g. procfs, this value is always
++ // 0. Also the link might have changed before the readlink call.
++ const int kBufferSize = PATH_MAX + 1;
++ char target[kBufferSize];
++ size_t target_size = ReadLinkInto(pathname, target, kBufferSize);
++ if (target_size <= 0) {
++ return NULL;
++ }
++ char* target_name = DartUtils::ScopedCString(target_size);
++ ASSERT(target_name != NULL);
++ memmove(target_name, target, target_size);
++ return target_name;
++}
++
++const char* File::GetCanonicalPath(Namespace* namespc,
++ const char* name,
++ char* dest,
++ int dest_size) {
++ if (name == NULL) {
++ return NULL;
++ }
++ if (!Namespace::IsDefault(namespc)) {
++ // TODO(zra): There is no realpathat(). Also chasing a symlink might result
++ // in a path to something outside of the namespace, so canonicalizing paths
++ // would have to be done carefully. For now, don't do anything.
++ return name;
++ }
++ char* abs_path;
++ if (dest == NULL) {
++ dest = DartUtils::ScopedCString(PATH_MAX + 1);
++ } else {
++ ASSERT(dest_size >= PATH_MAX);
++ }
++ ASSERT(dest != NULL);
++ do {
++ abs_path = realpath(name, dest);
++ } while ((abs_path == NULL) && (errno == EINTR));
++ ASSERT(abs_path == NULL || IsAbsolutePath(abs_path));
++ ASSERT(abs_path == NULL || (abs_path == dest));
++ return abs_path;
++}
++
++const char* File::PathSeparator() {
++ return "/";
++}
++
++const char* File::StringEscapedPathSeparator() {
++ return "/";
++}
++
++File::StdioHandleType File::GetStdioHandleType(int fd) {
++ struct stat64 buf;
++ int result = TEMP_FAILURE_RETRY(fstat64(fd, &buf));
++ if (result == -1) {
++ return kTypeError;
++ }
++ if (S_ISCHR(buf.st_mode)) {
++ return kTerminal;
++ }
++ if (S_ISFIFO(buf.st_mode)) {
++ return kPipe;
++ }
++ if (S_ISSOCK(buf.st_mode)) {
++ return kSocket;
++ }
++ if (S_ISREG(buf.st_mode)) {
++ return kFile;
++ }
++ return kOther;
++}
++
++File::Identical File::AreIdentical(Namespace* namespc_1,
++ const char* file_1,
++ Namespace* namespc_2,
++ const char* file_2) {
++ struct stat64 file_1_info;
++ struct stat64 file_2_info;
++ int status;
++ {
++ NamespaceScope ns1(namespc_1, file_1);
++ status = TEMP_FAILURE_RETRY(
++ fstatat64(ns1.fd(), ns1.path(), &file_1_info, AT_SYMLINK_NOFOLLOW));
++ if (status == -1) {
++ return File::kError;
++ }
++ }
++ {
++ NamespaceScope ns2(namespc_2, file_2);
++ status = TEMP_FAILURE_RETRY(
++ fstatat64(ns2.fd(), ns2.path(), &file_2_info, AT_SYMLINK_NOFOLLOW));
++ if (status == -1) {
++ return File::kError;
++ }
++ }
++ return ((file_1_info.st_ino == file_2_info.st_ino) &&
++ (file_1_info.st_dev == file_2_info.st_dev))
++ ? File::kIdentical
++ : File::kDifferent;
++}
++
++} // namespace bin
++} // namespace dart
++
++#endif // defined(DART_HOST_OS_OHOS)
+diff --git a/runtime/bin/file_system_watcher_ohos.cc b/runtime/bin/file_system_watcher_ohos.cc
+new file mode 100644
+index 00000000000..fa030a251e8
+--- /dev/null
++++ b/runtime/bin/file_system_watcher_ohos.cc
+@@ -0,0 +1,149 @@
++// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
++// for details. All rights reserved. Use of this source code is governed by a
++// BSD-style license that can be found in the LICENSE file.
++
++#include "platform/globals.h"
++#if defined(DART_HOST_OS_OHOS)
++
++#include "bin/file_system_watcher.h"
++
++#include // NOLINT
++#include // NOLINT
++
++#include "bin/fdutils.h"
++#include "bin/file.h"
++#include "bin/socket.h"
++#include "platform/signal_blocker.h"
++
++namespace dart {
++namespace bin {
++
++bool FileSystemWatcher::IsSupported() {
++ return true;
++}
++
++intptr_t FileSystemWatcher::Init() {
++ int id = NO_RETRY_EXPECTED(inotify_init1(IN_CLOEXEC));
++ if (id < 0) {
++ return -1;
++ }
++ // Some systems dosn't support setting this as non-blocking. Since watching
++ // internals are kept away from the user, we know it's possible to continue,
++ // even if setting non-blocking fails.
++ FDUtils::SetNonBlocking(id);
++ return id;
++}
++
++void FileSystemWatcher::Close(intptr_t id) {
++ USE(id);
++}
++
++intptr_t FileSystemWatcher::WatchPath(intptr_t id,
++ Namespace* namespc,
++ const char* path,
++ int events,
++ bool recursive) {
++ int list_events = IN_DELETE_SELF | IN_MOVE_SELF;
++ if ((events & kCreate) != 0) {
++ list_events |= IN_CREATE;
++ }
++ if ((events & kModifyContent) != 0) {
++ list_events |= IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY;
++ }
++ if ((events & kDelete) != 0) {
++ list_events |= IN_DELETE;
++ }
++ if ((events & kMove) != 0) {
++ list_events |= IN_MOVE;
++ }
++ const char* resolved_path = File::GetCanonicalPath(namespc, path);
++ path = resolved_path != NULL ? resolved_path : path;
++ int path_id = NO_RETRY_EXPECTED(inotify_add_watch(id, path, list_events));
++ if (path_id < 0) {
++ return -1;
++ }
++ return path_id;
++}
++
++void FileSystemWatcher::UnwatchPath(intptr_t id, intptr_t path_id) {
++ VOID_NO_RETRY_EXPECTED(inotify_rm_watch(id, path_id));
++}
++
++intptr_t FileSystemWatcher::GetSocketId(intptr_t id, intptr_t path_id) {
++ USE(path_id);
++ return id;
++}
++
++static int InotifyEventToMask(struct inotify_event* e) {
++ int mask = 0;
++ if ((e->mask & IN_CLOSE_WRITE) != 0 || (e->mask & IN_MODIFY) != 0) {
++ mask |= FileSystemWatcher::kModifyContent;
++ }
++ if ((e->mask & IN_ATTRIB) != 0) {
++ mask |= FileSystemWatcher::kModifyAttribute;
++ }
++ if ((e->mask & IN_CREATE) != 0) {
++ mask |= FileSystemWatcher::kCreate;
++ }
++ if ((e->mask & IN_MOVE) != 0) {
++ mask |= FileSystemWatcher::kMove;
++ }
++ if ((e->mask & IN_DELETE) != 0) {
++ mask |= FileSystemWatcher::kDelete;
++ }
++ if ((e->mask & (IN_DELETE_SELF | IN_MOVE_SELF)) != 0) {
++ mask |= FileSystemWatcher::kDeleteSelf;
++ }
++ if ((e->mask & IN_ISDIR) != 0) {
++ mask |= FileSystemWatcher::kIsDir;
++ }
++ return mask;
++}
++
++Dart_Handle FileSystemWatcher::ReadEvents(intptr_t id, intptr_t path_id) {
++ USE(path_id);
++ const intptr_t kEventSize = sizeof(struct inotify_event);
++ const intptr_t kBufferSize = kEventSize + NAME_MAX + 1;
++ uint8_t buffer[kBufferSize];
++ intptr_t bytes =
++ SocketBase::Read(id, buffer, kBufferSize, SocketBase::kAsync);
++ if (bytes < 0) {
++ return DartUtils::NewDartOSError();
++ }
++ const intptr_t kMaxCount = bytes / kEventSize;
++ Dart_Handle events = Dart_NewList(kMaxCount);
++ intptr_t offset = 0;
++ intptr_t i = 0;
++ while (offset < bytes) {
++ struct inotify_event* e =
++ reinterpret_cast(buffer + offset);
++ if ((e->mask & IN_IGNORED) == 0) {
++ Dart_Handle event = Dart_NewList(5);
++ int mask = InotifyEventToMask(e);
++ Dart_ListSetAt(event, 0, Dart_NewInteger(mask));
++ Dart_ListSetAt(event, 1, Dart_NewInteger(e->cookie));
++ if (e->len > 0) {
++ Dart_Handle name = Dart_NewStringFromUTF8(
++ reinterpret_cast(e->name), strlen(e->name));
++ if (Dart_IsError(name)) {
++ return name;
++ }
++ Dart_ListSetAt(event, 2, name);
++ } else {
++ Dart_ListSetAt(event, 2, Dart_Null());
++ }
++ Dart_ListSetAt(event, 3, Dart_NewBoolean((e->mask & IN_MOVED_TO) != 0u));
++ Dart_ListSetAt(event, 4, Dart_NewInteger(e->wd));
++ Dart_ListSetAt(events, i, event);
++ i++;
++ }
++ offset += kEventSize + e->len;
++ }
++ ASSERT(offset == bytes);
++ return events;
++}
++
++} // namespace bin
++} // namespace dart
++
++#endif // defined(DART_HOST_OS_OHOS)
+diff --git a/runtime/bin/io_impl_sources.gni b/runtime/bin/io_impl_sources.gni
+index d3ace672e9b..8367282a1ad 100644
+--- a/runtime/bin/io_impl_sources.gni
++++ b/runtime/bin/io_impl_sources.gni
+@@ -14,6 +14,8 @@ io_impl_sources = [
+ "eventhandler_fuchsia.h",
+ "eventhandler_linux.cc",
+ "eventhandler_linux.h",
++ "eventhandler_ohos.cc",
++ "eventhandler_ohos.h",
+ "eventhandler_macos.cc",
+ "eventhandler_macos.h",
+ "eventhandler_win.cc",
+@@ -22,6 +24,7 @@ io_impl_sources = [
+ "file_system_watcher.h",
+ "file_system_watcher_fuchsia.cc",
+ "file_system_watcher_linux.cc",
++ "file_system_watcher_ohos.cc",
+ "file_system_watcher_macos.cc",
+ "file_system_watcher_win.cc",
+ "filter.cc",
+@@ -37,6 +40,7 @@ io_impl_sources = [
+ "namespace_fuchsia.cc",
+ "namespace_fuchsia.h",
+ "namespace_linux.cc",
++ "namespace_ohos.cc",
+ "namespace_macos.cc",
+ "namespace_win.cc",
+ "platform.cc",
+@@ -50,6 +54,7 @@ io_impl_sources = [
+ "process.h",
+ "process_fuchsia.cc",
+ "process_linux.cc",
++ "process_ohos.cc",
+ "process_macos.cc",
+ "process_win.cc",
+ "reference_counting.h",
+@@ -63,6 +68,7 @@ io_impl_sources = [
+ "security_context.h",
+ "security_context_fuchsia.cc",
+ "security_context_linux.cc",
++ "security_context_ohos.cc",
+ "security_context_macos.cc",
+ "security_context_win.cc",
+ "socket.cc",
+@@ -80,18 +86,21 @@ io_impl_sources = [
+ "socket_base_win.h",
+ "socket_fuchsia.cc",
+ "socket_linux.cc",
++ "socket_ohos.cc",
+ "socket_macos.cc",
+ "socket_win.cc",
+ "stdio.cc",
+ "stdio.h",
+ "stdio_fuchsia.cc",
+ "stdio_linux.cc",
++ "stdio_ohos.cc",
+ "stdio_macos.cc",
+ "stdio_win.cc",
+ "sync_socket.cc",
+ "sync_socket.h",
+ "sync_socket_fuchsia.cc",
+ "sync_socket_linux.cc",
++ "sync_socket_ohos.cc",
+ "sync_socket_macos.cc",
+ "sync_socket_win.cc",
+ "typed_data_utils.cc",
+diff --git a/runtime/bin/main_options.cc b/runtime/bin/main_options.cc
+index 25d484c60c0..7e4ba510d9e 100644
+--- a/runtime/bin/main_options.cc
++++ b/runtime/bin/main_options.cc
+@@ -239,7 +239,7 @@ void Options::PrintUsage() {
+ " use for secure socket connections.\n"
+ #if defined(DART_HOST_OS_LINUX) || \
+ defined(DART_HOST_OS_ANDROID) || \
+- defined(DART_HOST_OS_FUCHSIA)
++ defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS)
+ "--namespace=\n"
+ " The path to a directory that dart:io calls will treat as the root of the\n"
+ " filesystem.\n"
+diff --git a/runtime/bin/namespace_ohos.cc b/runtime/bin/namespace_ohos.cc
+new file mode 100644
+index 00000000000..71b68ca618d
+--- /dev/null
++++ b/runtime/bin/namespace_ohos.cc
+@@ -0,0 +1,159 @@
++// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
++// for details. All rights reserved. Use of this source code is governed by a
++// BSD-style license that can be found in the LICENSE file.
++
++#include "platform/globals.h"
++#if defined(DART_HOST_OS_OHOS)
++
++#include "bin/namespace.h"
++
++#include
++#include
++
++#include "bin/file.h"
++#include "platform/signal_blocker.h"
++#include "platform/text_buffer.h"
++
++namespace dart {
++namespace bin {
++
++class NamespaceImpl {
++ public:
++ explicit NamespaceImpl(intptr_t rootfd) : rootfd_(rootfd), cwd_(strdup("/")) {
++ ASSERT(rootfd_ > 0);
++ cwdfd_ = dup(rootfd_);
++ ASSERT(cwdfd_ > 0);
++ }
++
++ explicit NamespaceImpl(const char* path)
++ : rootfd_(TEMP_FAILURE_RETRY(open64(path, O_DIRECTORY))),
++ cwd_(strdup("/")) {
++ ASSERT(rootfd_ > 0);
++ cwdfd_ = dup(rootfd_);
++ ASSERT(cwdfd_ > 0);
++ }
++
++ ~NamespaceImpl() {
++ NO_RETRY_EXPECTED(close(rootfd_));
++ free(cwd_);
++ NO_RETRY_EXPECTED(close(cwdfd_));
++ }
++
++ intptr_t rootfd() const { return rootfd_; }
++ char* cwd() const { return cwd_; }
++ intptr_t cwdfd() const { return cwdfd_; }
++
++ bool SetCwd(Namespace* namespc, const char* new_path) {
++ NamespaceScope ns(namespc, new_path);
++ const intptr_t new_cwdfd =
++ TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), O_DIRECTORY));
++ if (new_cwdfd < 0) {
++ return false;
++ }
++
++ TextBuffer tbuf(PATH_MAX);
++ if (!File::IsAbsolutePath(new_path)) {
++ tbuf.AddString(cwd_);
++ }
++ tbuf.AddString(File::PathSeparator());
++ tbuf.AddString(ns.path());
++
++ // Normalize it.
++ char result[PATH_MAX];
++ const intptr_t result_len =
++ File::CleanUnixPath(tbuf.buffer(), result, PATH_MAX);
++ if (result_len < 0) {
++ errno = ENAMETOOLONG;
++ return false;
++ }
++
++ free(cwd_);
++ cwd_ = strdup(result);
++ close(cwdfd_);
++ cwdfd_ = new_cwdfd;
++ return true;
++ }
++
++ private:
++ intptr_t rootfd_; // dirfd for the namespace root.
++ char* cwd_; // cwd relative to the namespace.
++ intptr_t cwdfd_; // dirfd for the cwd.
++
++ DISALLOW_COPY_AND_ASSIGN(NamespaceImpl);
++};
++
++Namespace* Namespace::Create(intptr_t namespc) {
++ NamespaceImpl* namespc_impl = NULL;
++ if (namespc != kNone) {
++ namespc_impl = new NamespaceImpl(namespc);
++ }
++ return new Namespace(namespc_impl);
++}
++
++Namespace* Namespace::Create(const char* path) {
++ return new Namespace(new NamespaceImpl(path));
++}
++
++Namespace::~Namespace() {
++ delete namespc_;
++}
++
++intptr_t Namespace::Default() {
++ return kNone;
++}
++
++const char* Namespace::GetCurrent(Namespace* namespc) {
++ if (Namespace::IsDefault(namespc)) {
++ char buffer[PATH_MAX];
++ if (getcwd(buffer, PATH_MAX) == NULL) {
++ return NULL;
++ }
++ return DartUtils::ScopedCopyCString(buffer);
++ }
++ const char* cwd = namespc->namespc()->cwd();
++ return cwd;
++}
++
++bool Namespace::SetCurrent(Namespace* namespc, const char* path) {
++ if (Namespace::IsDefault(namespc)) {
++ return (NO_RETRY_EXPECTED(chdir(path)) == 0);
++ }
++ return namespc->namespc()->SetCwd(namespc, path);
++}
++
++void Namespace::ResolvePath(Namespace* namespc,
++ const char* path,
++ intptr_t* dirfd,
++ const char** resolved_path) {
++ ASSERT(dirfd != NULL);
++ ASSERT(resolved_path != NULL);
++ if (Namespace::IsDefault(namespc)) {
++ *dirfd = AT_FDCWD;
++ *resolved_path = path;
++ return;
++ }
++ if (File::IsAbsolutePath(path)) {
++ *dirfd = namespc->namespc()->rootfd();
++ if (strcmp(path, File::PathSeparator()) == 0) {
++ // Change "/" to ".".
++ *resolved_path = ".";
++ } else {
++ // Otherwise strip off the leading "/".
++ *resolved_path = &path[1];
++ }
++ } else {
++ *dirfd = namespc->namespc()->cwdfd();
++ *resolved_path = path;
++ }
++}
++
++NamespaceScope::NamespaceScope(Namespace* namespc, const char* path) {
++ Namespace::ResolvePath(namespc, path, &fd_, &path_);
++}
++
++NamespaceScope::~NamespaceScope() {}
++
++} // namespace bin
++} // namespace dart
++
++#endif // defined(DART_HOST_OS_OHOS)
+diff --git a/runtime/bin/platform_linux.cc b/runtime/bin/platform_linux.cc
+index 4fed598047c..fc53fda3deb 100644
+--- a/runtime/bin/platform_linux.cc
++++ b/runtime/bin/platform_linux.cc
+@@ -3,7 +3,7 @@
+ // BSD-style license that can be found in the LICENSE file.
+
+ #include "platform/globals.h"
+-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS)
+
+ #include "bin/platform.h"
+
+diff --git a/runtime/bin/process.h b/runtime/bin/process.h
+index 9bd547c802b..6f9db260470 100644
+--- a/runtime/bin/process.h
++++ b/runtime/bin/process.h
+@@ -350,7 +350,7 @@ class BufferListBase {
+ };
+
+ #if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || \
+- defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS)
++ defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS)
+ class BufferList : public BufferListBase {
+ public:
+ BufferList() {}
+diff --git a/runtime/bin/process_ohos.cc b/runtime/bin/process_ohos.cc
+new file mode 100644
+index 00000000000..30c99dedae7
+--- /dev/null
++++ b/runtime/bin/process_ohos.cc
+@@ -0,0 +1,1165 @@
++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
++// for details. All rights reserved. Use of this source code is governed by a
++// BSD-style license that can be found in the LICENSE file.
++
++#include "platform/globals.h"
++#if defined(DART_HOST_OS_OHOS)
++
++#include "bin/process.h"
++
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++
++#include "bin/dartutils.h"
++#include "bin/directory.h"
++#include "bin/fdutils.h"
++#include "bin/file.h"
++#include "bin/lockers.h"
++#include "bin/reference_counting.h"
++#include "bin/thread.h"
++#include "platform/syslog.h"
++
++#include "platform/signal_blocker.h"
++#include "platform/utils.h"
++
++extern char** environ;
++
++namespace dart {
++namespace bin {
++
++int Process::global_exit_code_ = 0;
++Mutex* Process::global_exit_code_mutex_ = nullptr;
++Process::ExitHook Process::exit_hook_ = NULL;
++
++// ProcessInfo is used to map a process id to the file descriptor for
++// the pipe used to communicate the exit code of the process to Dart.
++// ProcessInfo objects are kept in the static singly-linked
++// ProcessInfoList.
++class ProcessInfo {
++ public:
++ ProcessInfo(pid_t pid, intptr_t fd) : pid_(pid), fd_(fd) {}
++ ~ProcessInfo() {
++ int closed = close(fd_);
++ if (closed != 0) {
++ FATAL("Failed to close process exit code pipe");
++ }
++ }
++ pid_t pid() { return pid_; }
++ intptr_t fd() { return fd_; }
++ ProcessInfo* next() { return next_; }
++ void set_next(ProcessInfo* info) { next_ = info; }
++
++ private:
++ pid_t pid_;
++ intptr_t fd_;
++ ProcessInfo* next_;
++
++ DISALLOW_COPY_AND_ASSIGN(ProcessInfo);
++};
++
++// Singly-linked list of ProcessInfo objects for all active processes
++// started from Dart.
++class ProcessInfoList {
++ public:
++ static void Init();
++ static void Cleanup();
++
++ static void AddProcess(pid_t pid, intptr_t fd) {
++ MutexLocker locker(mutex_);
++ ProcessInfo* info = new ProcessInfo(pid, fd);
++ info->set_next(active_processes_);
++ active_processes_ = info;
++ }
++
++ static intptr_t LookupProcessExitFd(pid_t pid) {
++ MutexLocker locker(mutex_);
++ ProcessInfo* current = active_processes_;
++ while (current != NULL) {
++ if (current->pid() == pid) {
++ return current->fd();
++ }
++ current = current->next();
++ }
++ return 0;
++ }
++
++ static void RemoveProcess(pid_t pid) {
++ MutexLocker locker(mutex_);
++ ProcessInfo* prev = NULL;
++ ProcessInfo* current = active_processes_;
++ while (current != NULL) {
++ if (current->pid() == pid) {
++ if (prev == NULL) {
++ active_processes_ = current->next();
++ } else {
++ prev->set_next(current->next());
++ }
++ delete current;
++ return;
++ }
++ prev = current;
++ current = current->next();
++ }
++ }
++
++ private:
++ // Linked list of ProcessInfo objects for all active processes
++ // started from Dart code.
++ static ProcessInfo* active_processes_;
++ // Mutex protecting all accesses to the linked list of active
++ // processes.
++ static Mutex* mutex_;
++
++ DISALLOW_ALLOCATION();
++ DISALLOW_IMPLICIT_CONSTRUCTORS(ProcessInfoList);
++};
++
++ProcessInfo* ProcessInfoList::active_processes_ = NULL;
++Mutex* ProcessInfoList::mutex_ = nullptr;
++
++// The exit code handler sets up a separate thread which waits for child
++// processes to terminate. That separate thread can then get the exit code from
++// processes that have exited and communicate it to Dart through the
++// event loop.
++class ExitCodeHandler {
++ public:
++ static void Init();
++ static void Cleanup();
++
++ // Notify the ExitCodeHandler that another process exists.
++ static void ProcessStarted() {
++ // Multiple isolates could be starting processes at the same
++ // time. Make sure that only one ExitCodeHandler thread exists.
++ MonitorLocker locker(monitor_);
++ process_count_++;
++
++ monitor_->Notify();
++
++ if (running_) {
++ return;
++ }
++
++ // Start thread that handles process exits when wait returns.
++ int result =
++ Thread::Start("dart:io Process.start", ExitCodeHandlerEntry, 0);
++ if (result != 0) {
++ FATAL("Failed to start exit code handler worker thread %d", result);
++ }
++
++ running_ = true;
++ }
++
++ static void TerminateExitCodeThread() {
++ MonitorLocker locker(monitor_);
++
++ if (!running_) {
++ return;
++ }
++
++ // Set terminate_done_ to false, so we can use it as a guard for our
++ // monitor.
++ running_ = false;
++
++ // Wake up the [ExitCodeHandler] thread which is blocked on `wait()` (see
++ // [ExitCodeHandlerEntry]).
++ if (TEMP_FAILURE_RETRY(fork()) == 0) {
++ // Avoid calling any atexit callbacks to prevent deadlocks.
++ _exit(0);
++ }
++
++ monitor_->Notify();
++
++ while (!terminate_done_) {
++ monitor_->Wait(Monitor::kNoTimeout);
++ }
++ }
++
++ private:
++ // Entry point for the separate exit code handler thread started by
++ // the ExitCodeHandler.
++ static void ExitCodeHandlerEntry(uword param) {
++ pid_t pid = 0;
++ int status = 0;
++ while (true) {
++ {
++ MonitorLocker locker(monitor_);
++ while (running_ && process_count_ == 0) {
++ monitor_->Wait(Monitor::kNoTimeout);
++ }
++ if (!running_) {
++ terminate_done_ = true;
++ monitor_->Notify();
++ return;
++ }
++ }
++
++ if ((pid = TEMP_FAILURE_RETRY(wait(&status))) > 0) {
++ int exit_code = 0;
++ int negative = 0;
++ if (WIFEXITED(status)) {
++ exit_code = WEXITSTATUS(status);
++ }
++ if (WIFSIGNALED(status)) {
++ exit_code = WTERMSIG(status);
++ negative = 1;
++ }
++ intptr_t exit_code_fd = ProcessInfoList::LookupProcessExitFd(pid);
++ if (exit_code_fd != 0) {
++ int message[2] = {exit_code, negative};
++ ssize_t result =
++ FDUtils::WriteToBlocking(exit_code_fd, &message, sizeof(message));
++ // If the process has been closed, the read end of the exit
++ // pipe has been closed. It is therefore not a problem that
++ // write fails with a broken pipe error. Other errors should
++ // not happen.
++ if ((result != -1) && (result != sizeof(message))) {
++ FATAL("Failed to write entire process exit message");
++ } else if ((result == -1) && (errno != EPIPE)) {
++ FATAL("Failed to write exit code: %d", errno);
++ }
++ ProcessInfoList::RemoveProcess(pid);
++ {
++ MonitorLocker locker(monitor_);
++ process_count_--;
++ }
++ }
++ } else if (pid < 0) {
++ FATAL("Wait for process exit failed: %d", errno);
++ }
++ }
++ }
++
++ static bool terminate_done_;
++ static int process_count_;
++ static bool running_;
++ static Monitor* monitor_;
++
++ DISALLOW_ALLOCATION();
++ DISALLOW_IMPLICIT_CONSTRUCTORS(ExitCodeHandler);
++};
++
++bool ExitCodeHandler::running_ = false;
++int ExitCodeHandler::process_count_ = 0;
++bool ExitCodeHandler::terminate_done_ = false;
++Monitor* ExitCodeHandler::monitor_ = nullptr;
++
++class ProcessStarter {
++ public:
++ ProcessStarter(Namespace* namespc,
++ const char* path,
++ char* arguments[],
++ intptr_t arguments_length,
++ const char* working_directory,
++ char* environment[],
++ intptr_t environment_length,
++ ProcessStartMode mode,
++ intptr_t* in,
++ intptr_t* out,
++ intptr_t* err,
++ intptr_t* id,
++ intptr_t* exit_event,
++ char** os_error_message)
++ : namespc_(namespc),
++ path_(path),
++ working_directory_(working_directory),
++ mode_(mode),
++ in_(in),
++ out_(out),
++ err_(err),
++ id_(id),
++ exit_event_(exit_event),
++ os_error_message_(os_error_message) {
++ read_in_[0] = -1;
++ read_in_[1] = -1;
++ read_err_[0] = -1;
++ read_err_[1] = -1;
++ write_out_[0] = -1;
++ write_out_[1] = -1;
++ exec_control_[0] = -1;
++ exec_control_[1] = -1;
++
++ program_arguments_ = reinterpret_cast(Dart_ScopeAllocate(
++ (arguments_length + 2) * sizeof(*program_arguments_)));
++ program_arguments_[0] = const_cast(path_);
++ for (int i = 0; i < arguments_length; i++) {
++ program_arguments_[i + 1] = arguments[i];
++ }
++ program_arguments_[arguments_length + 1] = NULL;
++
++ program_environment_ = NULL;
++ if (environment != NULL) {
++ program_environment_ = reinterpret_cast(Dart_ScopeAllocate(
++ (environment_length + 1) * sizeof(*program_environment_)));
++ for (int i = 0; i < environment_length; i++) {
++ program_environment_[i] = environment[i];
++ }
++ program_environment_[environment_length] = NULL;
++ }
++ }
++
++ int Start() {
++ // Create pipes required.
++ int err = CreatePipes();
++ if (err != 0) {
++ return err;
++ }
++
++ // Fork to create the new process.
++ pid_t pid = TEMP_FAILURE_RETRY(fork());
++ if (pid < 0) {
++ // Failed to fork.
++ return CleanupAndReturnError();
++ } else if (pid == 0) {
++ // This runs in the new process.
++ NewProcess();
++ }
++
++ // This runs in the original process.
++
++ // If the child process is not started in detached mode, be sure to
++ // listen for exit-codes, now that we have a non detached child process
++ // and also Register this child process.
++ if (Process::ModeIsAttached(mode_)) {
++ ExitCodeHandler::ProcessStarted();
++ err = RegisterProcess(pid);
++ if (err != 0) {
++ return err;
++ }
++ }
++
++ // Notify child process to start. This is done to delay the call to exec
++ // until the process is registered above, and we are ready to receive the
++ // exit code.
++ char msg = '1';
++ int bytes_written =
++ FDUtils::WriteToBlocking(read_in_[1], &msg, sizeof(msg));
++ if (bytes_written != sizeof(msg)) {
++ return CleanupAndReturnError();
++ }
++
++ // Read the result of executing the child process.
++ close(exec_control_[1]);
++ exec_control_[1] = -1;
++ if (Process::ModeIsAttached(mode_)) {
++ err = ReadExecResult();
++ } else {
++ err = ReadDetachedExecResult(&pid);
++ }
++ close(exec_control_[0]);
++ exec_control_[0] = -1;
++
++ // Return error code if any failures.
++ if (err != 0) {
++ if (Process::ModeIsAttached(mode_)) {
++ // Since exec() failed, we're not interested in the exit code.
++ // We close the reading side of the exit code pipe here.
++ // GetProcessExitCodes will get a broken pipe error when it
++ // tries to write to the writing side of the pipe and it will
++ // ignore the error.
++ close(*exit_event_);
++ *exit_event_ = -1;
++ }
++ CloseAllPipes();
++ return err;
++ }
++
++ if (Process::ModeHasStdio(mode_)) {
++ // Connect stdio, stdout and stderr.
++ FDUtils::SetNonBlocking(read_in_[0]);
++ *in_ = read_in_[0];
++ close(read_in_[1]);
++ FDUtils::SetNonBlocking(write_out_[1]);
++ *out_ = write_out_[1];
++ close(write_out_[0]);
++ FDUtils::SetNonBlocking(read_err_[0]);
++ *err_ = read_err_[0];
++ close(read_err_[1]);
++ } else {
++ // Close all fds.
++ close(read_in_[0]);
++ close(read_in_[1]);
++ ASSERT(write_out_[0] == -1);
++ ASSERT(write_out_[1] == -1);
++ ASSERT(read_err_[0] == -1);
++ ASSERT(read_err_[1] == -1);
++ }
++ ASSERT(exec_control_[0] == -1);
++ ASSERT(exec_control_[1] == -1);
++
++ *id_ = pid;
++ return 0;
++ }
++
++ private:
++ static constexpr int kErrorBufferSize = 1024;
++
++ int CreatePipes() {
++ int result;
++ result = TEMP_FAILURE_RETRY(pipe2(exec_control_, O_CLOEXEC));
++ if (result < 0) {
++ return CleanupAndReturnError();
++ }
++
++ // For a detached process the pipe to connect stdout is still used for
++ // signaling when to do the first fork.
++ result = TEMP_FAILURE_RETRY(pipe2(read_in_, O_CLOEXEC));
++ if (result < 0) {
++ return CleanupAndReturnError();
++ }
++
++ // For detached processes the pipe to connect stderr and stdin are not used.
++ if (Process::ModeHasStdio(mode_)) {
++ result = TEMP_FAILURE_RETRY(pipe2(read_err_, O_CLOEXEC));
++ if (result < 0) {
++ return CleanupAndReturnError();
++ }
++
++ result = TEMP_FAILURE_RETRY(pipe2(write_out_, O_CLOEXEC));
++ if (result < 0) {
++ return CleanupAndReturnError();
++ }
++ }
++
++ return 0;
++ }
++
++ void NewProcess() {
++ // Wait for parent process before setting up the child process.
++ char msg;
++ int bytes_read = FDUtils::ReadFromBlocking(read_in_[0], &msg, sizeof(msg));
++ if (bytes_read != sizeof(msg)) {
++ perror("Failed receiving notification message");
++ _exit(1);
++ }
++ if (Process::ModeIsAttached(mode_)) {
++ ExecProcess();
++ } else {
++ ExecDetachedProcess();
++ }
++ }
++
++ // Tries to find path_ relative to the current namespace unless it should be
++ // searched in the PATH.
++ // The path that should be passed to exec is returned in realpath.
++ // Returns true on success, and false if there was an error that should
++ // be reported to the parent.
++ bool FindPathInNamespace(char* realpath, intptr_t realpath_size) {
++ // Perform a PATH search if there's no slash in the path.
++ if (strchr(path_, '/') == NULL) {
++ // TODO(zra): If there is a non-default namespace, the entries in PATH
++ // should be treated as relative to the namespace.
++ strncpy(realpath, path_, realpath_size);
++ realpath[realpath_size - 1] = '\0';
++ return true;
++ }
++ NamespaceScope ns(namespc_, path_);
++ const int fd =
++ TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), O_RDONLY | O_CLOEXEC));
++ if (fd == -1) {
++ return false;
++ }
++ char procpath[PATH_MAX];
++ snprintf(procpath, PATH_MAX, "/proc/self/fd/%d", fd);
++ const intptr_t length =
++ TEMP_FAILURE_RETRY(readlink(procpath, realpath, realpath_size));
++ if (length < 0) {
++ FDUtils::SaveErrorAndClose(fd);
++ return false;
++ }
++ realpath[length] = '\0';
++ FDUtils::SaveErrorAndClose(fd);
++ return true;
++ }
++
++ void ExecProcess() {
++ if (mode_ == kNormal) {
++ if (TEMP_FAILURE_RETRY(dup2(write_out_[0], STDIN_FILENO)) == -1) {
++ ReportChildError();
++ }
++
++ if (TEMP_FAILURE_RETRY(dup2(read_in_[1], STDOUT_FILENO)) == -1) {
++ ReportChildError();
++ }
++
++ if (TEMP_FAILURE_RETRY(dup2(read_err_[1], STDERR_FILENO)) == -1) {
++ ReportChildError();
++ }
++ } else {
++ ASSERT(mode_ == kInheritStdio);
++ }
++
++ if (working_directory_ != NULL &&
++ !Directory::SetCurrent(namespc_, working_directory_)) {
++ ReportChildError();
++ }
++
++ if (program_environment_ != NULL) {
++ environ = program_environment_;
++ }
++
++ char realpath[PATH_MAX];
++ if (!FindPathInNamespace(realpath, PATH_MAX)) {
++ ReportChildError();
++ }
++ // TODO(dart:io) Test for the existence of execveat, and use it instead.
++ execvp(realpath, const_cast(program_arguments_));
++ ReportChildError();
++ }
++
++ void ExecDetachedProcess() {
++ if (mode_ == kDetached) {
++ ASSERT(write_out_[0] == -1);
++ ASSERT(write_out_[1] == -1);
++ ASSERT(read_err_[0] == -1);
++ ASSERT(read_err_[1] == -1);
++ // For a detached process the pipe to connect stdout is only used for
++ // signaling when to do the first fork.
++ close(read_in_[0]);
++ read_in_[0] = -1;
++ close(read_in_[1]);
++ read_in_[1] = -1;
++ } else {
++ // Don't close any fds if keeping stdio open to the detached process.
++ ASSERT(mode_ == kDetachedWithStdio);
++ }
++ // Fork once more to start a new session.
++ pid_t pid = TEMP_FAILURE_RETRY(fork());
++ if (pid < 0) {
++ ReportChildError();
++ } else if (pid == 0) {
++ // Start a new session.
++ if (TEMP_FAILURE_RETRY(setsid()) == -1) {
++ ReportChildError();
++ } else {
++ // Do a final fork to not be the session leader.
++ pid = TEMP_FAILURE_RETRY(fork());
++ if (pid < 0) {
++ ReportChildError();
++ } else if (pid == 0) {
++ if (mode_ == kDetached) {
++ SetupDetached();
++ } else {
++ SetupDetachedWithStdio();
++ }
++
++ if ((working_directory_ != NULL) &&
++ !Directory::SetCurrent(namespc_, working_directory_)) {
++ ReportChildError();
++ }
++ if (program_environment_ != NULL) {
++ environ = program_environment_;
++ }
++
++ // Report the final PID and do the exec.
++ ReportPid(getpid()); // getpid cannot fail.
++ char realpath[PATH_MAX];
++ if (!FindPathInNamespace(realpath, PATH_MAX)) {
++ ReportChildError();
++ }
++ // TODO(dart:io) Test for the existence of execveat, and use it
++ // instead.
++ execvp(realpath, const_cast(program_arguments_));
++ ReportChildError();
++ } else {
++ // Exit the intermediate process. Avoid calling any atexit callbacks
++ // to avoid potential issues (e.g. deadlocks).
++ _exit(0);
++ }
++ }
++ } else {
++ // Exit the intermediate process. Avoid calling any atexit callbacks
++ // to avoid potential issues (e.g. deadlocks).
++ _exit(0);
++ }
++ }
++
++ int RegisterProcess(pid_t pid) {
++ int result;
++ int event_fds[2];
++ result = TEMP_FAILURE_RETRY(pipe2(event_fds, O_CLOEXEC));
++ if (result < 0) {
++ return CleanupAndReturnError();
++ }
++
++ ProcessInfoList::AddProcess(pid, event_fds[1]);
++ *exit_event_ = event_fds[0];
++ FDUtils::SetNonBlocking(event_fds[0]);
++ return 0;
++ }
++
++ int ReadExecResult() {
++ int child_errno;
++ int bytes_read = -1;
++ // Read exec result from child. If no data is returned the exec was
++ // successful and the exec call closed the pipe. Otherwise the errno
++ // is written to the pipe.
++ bytes_read = FDUtils::ReadFromBlocking(exec_control_[0], &child_errno,
++ sizeof(child_errno));
++ if (bytes_read == sizeof(child_errno)) {
++ ReadChildError();
++ return child_errno;
++ } else if (bytes_read == -1) {
++ return errno;
++ }
++ return 0;
++ }
++
++ int ReadDetachedExecResult(pid_t* pid) {
++ int child_errno;
++ int bytes_read = -1;
++ // Read exec result from child. If only pid data is returned the exec was
++ // successful and the exec call closed the pipe. Otherwise the errno
++ // is written to the pipe as well.
++ int result[2];
++ bytes_read =
++ FDUtils::ReadFromBlocking(exec_control_[0], result, sizeof(result));
++ if (bytes_read == sizeof(int)) {
++ *pid = result[0];
++ } else if (bytes_read == 2 * sizeof(int)) {
++ *pid = result[0];
++ child_errno = result[1];
++ ReadChildError();
++ return child_errno;
++ } else if (bytes_read == -1) {
++ return errno;
++ }
++ return 0;
++ }
++
++ void SetupDetached() {
++ ASSERT(mode_ == kDetached);
++
++ // Close all open file descriptors except for exec_control_[1].
++ int max_fds = sysconf(_SC_OPEN_MAX);
++ if (max_fds == -1) {
++ max_fds = _POSIX_OPEN_MAX;
++ }
++ for (int fd = 0; fd < max_fds; fd++) {
++ if (fd != exec_control_[1]) {
++ close(fd);
++ }
++ }
++
++ // Re-open stdin, stdout and stderr and connect them to /dev/null.
++ // The loop above should already have closed all of them, so
++ // creating new file descriptors should start at STDIN_FILENO.
++ int fd = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR));
++ if (fd != STDIN_FILENO) {
++ ReportChildError();
++ }
++ if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDOUT_FILENO)) !=
++ STDOUT_FILENO) {
++ ReportChildError();
++ }
++ if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDERR_FILENO)) !=
++ STDERR_FILENO) {
++ ReportChildError();
++ }
++ }
++
++ void SetupDetachedWithStdio() {
++ // Close all open file descriptors except for
++ // exec_control_[1], write_out_[0], read_in_[1] and
++ // read_err_[1].
++ int max_fds = sysconf(_SC_OPEN_MAX);
++ if (max_fds == -1) {
++ max_fds = _POSIX_OPEN_MAX;
++ }
++ for (int fd = 0; fd < max_fds; fd++) {
++ if ((fd != exec_control_[1]) && (fd != write_out_[0]) &&
++ (fd != read_in_[1]) && (fd != read_err_[1])) {
++ close(fd);
++ }
++ }
++
++ if (TEMP_FAILURE_RETRY(dup2(write_out_[0], STDIN_FILENO)) == -1) {
++ ReportChildError();
++ }
++ close(write_out_[0]);
++
++ if (TEMP_FAILURE_RETRY(dup2(read_in_[1], STDOUT_FILENO)) == -1) {
++ ReportChildError();
++ }
++ close(read_in_[1]);
++
++ if (TEMP_FAILURE_RETRY(dup2(read_err_[1], STDERR_FILENO)) == -1) {
++ ReportChildError();
++ }
++ close(read_err_[1]);
++ }
++
++ int CleanupAndReturnError() {
++ int actual_errno = errno;
++ // If CleanupAndReturnError is called without an actual errno make
++ // sure to return an error anyway.
++ if (actual_errno == 0) {
++ actual_errno = EPERM;
++ }
++ SetChildOsErrorMessage();
++ CloseAllPipes();
++ return actual_errno;
++ }
++
++ void SetChildOsErrorMessage() {
++ char* error_message = DartUtils::ScopedCString(kErrorBufferSize);
++ Utils::StrError(errno, error_message, kErrorBufferSize);
++ *os_error_message_ = error_message;
++ }
++
++ void ReportChildError() {
++ // In the case of failure in the child process write the errno and
++ // the OS error message to the exec control pipe and exit.
++ int child_errno = errno;
++ char error_buf[kErrorBufferSize];
++ char* os_error_message =
++ Utils::StrError(errno, error_buf, kErrorBufferSize);
++ int bytes_written = FDUtils::WriteToBlocking(exec_control_[1], &child_errno,
++ sizeof(child_errno));
++ if (bytes_written == sizeof(child_errno)) {
++ FDUtils::WriteToBlocking(exec_control_[1], os_error_message,
++ strlen(os_error_message) + 1);
++ }
++ close(exec_control_[1]);
++
++ // We avoid running through registered atexit() handlers because that is
++ // unnecessary work. It can also cause deadlocks on exit in the forked
++ // process.
++ _exit(1);
++ }
++
++ void ReportPid(int pid) {
++ // In the case of starting a detached process the actual pid of that process
++ // is communicated using the exec control pipe.
++ int bytes_written =
++ FDUtils::WriteToBlocking(exec_control_[1], &pid, sizeof(pid));
++ ASSERT(bytes_written == sizeof(int));
++ USE(bytes_written);
++ }
++
++ void ReadChildError() {
++ char* message = DartUtils::ScopedCString(kErrorBufferSize);
++ if (message != NULL) {
++ FDUtils::ReadFromBlocking(exec_control_[0], message, kErrorBufferSize);
++ message[kErrorBufferSize - 1] = '\0';
++ *os_error_message_ = message;
++ } else {
++ // Could not get error message. It will be NULL.
++ ASSERT(*os_error_message_ == NULL);
++ }
++ }
++
++ void ClosePipe(int* fds) {
++ for (int i = 0; i < 2; i++) {
++ if (fds[i] != -1) {
++ close(fds[i]);
++ fds[i] = -1;
++ }
++ }
++ }
++
++ void CloseAllPipes() {
++ ClosePipe(exec_control_);
++ ClosePipe(read_in_);
++ ClosePipe(read_err_);
++ ClosePipe(write_out_);
++ }
++
++ int read_in_[2]; // Pipe for stdout to child process.
++ int read_err_[2]; // Pipe for stderr to child process.
++ int write_out_[2]; // Pipe for stdin to child process.
++ int exec_control_[2]; // Pipe to get the result from exec.
++
++ char** program_arguments_;
++ char** program_environment_;
++
++ Namespace* namespc_;
++ const char* path_;
++ const char* working_directory_;
++ ProcessStartMode mode_;
++ intptr_t* in_;
++ intptr_t* out_;
++ intptr_t* err_;
++ intptr_t* id_;
++ intptr_t* exit_event_;
++ char** os_error_message_;
++
++ DISALLOW_ALLOCATION();
++ DISALLOW_IMPLICIT_CONSTRUCTORS(ProcessStarter);
++};
++
++int Process::Start(Namespace* namespc,
++ const char* path,
++ char* arguments[],
++ intptr_t arguments_length,
++ const char* working_directory,
++ char* environment[],
++ intptr_t environment_length,
++ ProcessStartMode mode,
++ intptr_t* in,
++ intptr_t* out,
++ intptr_t* err,
++ intptr_t* id,
++ intptr_t* exit_event,
++ char** os_error_message) {
++ ProcessStarter starter(namespc, path, arguments, arguments_length,
++ working_directory, environment, environment_length,
++ mode, in, out, err, id, exit_event, os_error_message);
++ return starter.Start();
++}
++
++static bool CloseProcessBuffers(struct pollfd* fds, int alive) {
++ int e = errno;
++ for (int i = 0; i < alive; i++) {
++ close(fds[i].fd);
++ }
++ errno = e;
++ return false;
++}
++
++bool Process::Wait(intptr_t pid,
++ intptr_t in,
++ intptr_t out,
++ intptr_t err,
++ intptr_t exit_event,
++ ProcessResult* result) {
++ // Close input to the process right away.
++ close(in);
++
++ // There is no return from this function using Dart_PropagateError
++ // as memory used by the buffer lists is freed through their
++ // destructors.
++ BufferList out_data;
++ BufferList err_data;
++ union {
++ uint8_t bytes[8];
++ int32_t ints[2];
++ } exit_code_data;
++
++ struct pollfd fds[3];
++ fds[0].fd = out;
++ fds[1].fd = err;
++ fds[2].fd = exit_event;
++
++ for (int i = 0; i < 3; i++) {
++ fds[i].events = POLLIN;
++ }
++
++ int alive = 3;
++ while (alive > 0) {
++ // Blocking call waiting for events from the child process.
++ if (TEMP_FAILURE_RETRY(poll(fds, alive, -1)) <= 0) {
++ return CloseProcessBuffers(fds, alive);
++ }
++
++ // Process incoming data.
++ for (int i = 0; i < alive; i++) {
++ if ((fds[i].revents & (POLLNVAL | POLLERR)) != 0) {
++ return CloseProcessBuffers(fds, alive);
++ }
++ if ((fds[i].revents & POLLIN) != 0) {
++ intptr_t avail = FDUtils::AvailableBytes(fds[i].fd);
++ if (fds[i].fd == out) {
++ if (!out_data.Read(out, avail)) {
++ return CloseProcessBuffers(fds, alive);
++ }
++ } else if (fds[i].fd == err) {
++ if (!err_data.Read(err, avail)) {
++ return CloseProcessBuffers(fds, alive);
++ }
++ } else if (fds[i].fd == exit_event) {
++ if (avail == 8) {
++ intptr_t b =
++ TEMP_FAILURE_RETRY(read(exit_event, exit_code_data.bytes, 8));
++ if (b != 8) {
++ return CloseProcessBuffers(fds, alive);
++ }
++ }
++ } else {
++ UNREACHABLE();
++ }
++ }
++ if ((fds[i].revents & POLLHUP) != 0) {
++ // Remove the pollfd from the list of pollfds.
++ close(fds[i].fd);
++ alive--;
++ if (i < alive) {
++ fds[i] = fds[alive];
++ }
++ // Process the same index again.
++ i--;
++ continue;
++ }
++ }
++ }
++
++ // All handles closed and all data read.
++ result->set_stdout_data(out_data.GetData());
++ result->set_stderr_data(err_data.GetData());
++ DEBUG_ASSERT(out_data.IsEmpty());
++ DEBUG_ASSERT(err_data.IsEmpty());
++
++ // Calculate the exit code.
++ intptr_t exit_code = exit_code_data.ints[0];
++ intptr_t negative = exit_code_data.ints[1];
++ if (negative != 0) {
++ exit_code = -exit_code;
++ }
++ result->set_exit_code(exit_code);
++
++ return true;
++}
++
++bool Process::Kill(intptr_t id, int signal) {
++ return (TEMP_FAILURE_RETRY(kill(id, signal)) != -1);
++}
++
++void Process::TerminateExitCodeHandler() {
++ ExitCodeHandler::TerminateExitCodeThread();
++}
++
++intptr_t Process::CurrentProcessId() {
++ return static_cast(getpid());
++}
++
++static void SaveErrorAndClose(FILE* file) {
++ int actual_errno = errno;
++ fclose(file);
++ errno = actual_errno;
++}
++
++int64_t Process::CurrentRSS() {
++ // The second value in /proc/self/statm is the current RSS in pages.
++ // It is not possible to use getrusage() because the interested fields are not
++ // implemented by the linux kernel.
++ FILE* statm = fopen("/proc/self/statm", "r");
++ if (statm == NULL) {
++ return -1;
++ }
++ int64_t current_rss_pages = 0;
++ int matches = fscanf(statm, "%*s%" Pd64 "", ¤t_rss_pages);
++ if (matches != 1) {
++ SaveErrorAndClose(statm);
++ return -1;
++ }
++ fclose(statm);
++ return current_rss_pages * getpagesize();
++}
++
++int64_t Process::MaxRSS() {
++ struct rusage usage;
++ usage.ru_maxrss = 0;
++ int r = getrusage(RUSAGE_SELF, &usage);
++ if (r < 0) {
++ return -1;
++ }
++ return usage.ru_maxrss * KB;
++}
++
++static Mutex* signal_mutex = nullptr;
++static SignalInfo* signal_handlers = NULL;
++static const int kSignalsCount = 7;
++static const int kSignals[kSignalsCount] = {
++ SIGHUP, SIGINT, SIGTERM, SIGUSR1, SIGUSR2, SIGWINCH,
++ SIGQUIT // Allow VMService to listen on SIGQUIT.
++};
++
++SignalInfo::~SignalInfo() {
++ close(fd_);
++}
++
++static void SignalHandler(int signal) {
++ MutexLocker lock(signal_mutex);
++ const SignalInfo* handler = signal_handlers;
++ while (handler != NULL) {
++ if (handler->signal() == signal) {
++ int value = 0;
++ VOID_TEMP_FAILURE_RETRY(write(handler->fd(), &value, 1));
++ }
++ handler = handler->next();
++ }
++}
++
++intptr_t Process::SetSignalHandler(intptr_t signal) {
++ bool found = false;
++ for (int i = 0; i < kSignalsCount; i++) {
++ if (kSignals[i] == signal) {
++ found = true;
++ break;
++ }
++ }
++ if (!found) {
++ return -1;
++ }
++ int fds[2];
++ if (NO_RETRY_EXPECTED(pipe2(fds, O_CLOEXEC)) != 0) {
++ return -1;
++ }
++ ThreadSignalBlocker blocker(kSignalsCount, kSignals);
++ MutexLocker lock(signal_mutex);
++ SignalInfo* handler = signal_handlers;
++ bool listen = true;
++ sa_handler_t oldact_handler = nullptr;
++ while (handler != NULL) {
++ if (handler->signal() == signal) {
++ oldact_handler = handler->oldact();
++ listen = false;
++ break;
++ }
++ handler = handler->next();
++ }
++ if (listen) {
++ struct sigaction act = {};
++ act.sa_handler = SignalHandler;
++ sigemptyset(&act.sa_mask);
++ for (int i = 0; i < kSignalsCount; i++) {
++ sigaddset(&act.sa_mask, kSignals[i]);
++ }
++ struct sigaction oldact = {};
++ int status = NO_RETRY_EXPECTED(sigaction(signal, &act, &oldact));
++ if (status < 0) {
++ int err = errno;
++ close(fds[0]);
++ close(fds[1]);
++ errno = err;
++ return -1;
++ }
++ oldact_handler = oldact.sa_handler;
++ }
++ signal_handlers =
++ new SignalInfo(fds[1], signal, oldact_handler, signal_handlers);
++ return fds[0];
++}
++
++void Process::ClearSignalHandler(intptr_t signal, Dart_Port port) {
++ ThreadSignalBlocker blocker(kSignalsCount, kSignals);
++ MutexLocker lock(signal_mutex);
++ SignalInfo* handler = signal_handlers;
++ sa_handler_t oldact_handler = SIG_DFL;
++ bool any_removed = false;
++ bool any_remaining = false;
++ while (handler != NULL) {
++ bool remove = false;
++ if (handler->signal() == signal) {
++ if ((port == ILLEGAL_PORT) || (handler->port() == port)) {
++ if (signal_handlers == handler) {
++ signal_handlers = handler->next();
++ }
++ handler->Unlink();
++ remove = true;
++ oldact_handler = handler->oldact();
++ any_removed = true;
++ } else {
++ any_remaining = true;
++ }
++ }
++ SignalInfo* next = handler->next();
++ if (remove) {
++ delete handler;
++ }
++ handler = next;
++ }
++ if (any_removed && !any_remaining) {
++ struct sigaction act = {};
++ act.sa_handler = oldact_handler;
++ VOID_NO_RETRY_EXPECTED(sigaction(signal, &act, NULL));
++ }
++}
++
++void Process::ClearSignalHandlerByFd(intptr_t fd, Dart_Port port) {
++ ThreadSignalBlocker blocker(kSignalsCount, kSignals);
++ MutexLocker lock(signal_mutex);
++ SignalInfo* handler = signal_handlers;
++ sa_handler_t oldact_handler = SIG_DFL;
++ bool any_remaining = false;
++ intptr_t signal = -1;
++ while (handler != NULL) {
++ bool remove = false;
++ if (handler->fd() == fd) {
++ if ((port == ILLEGAL_PORT) || (handler->port() == port)) {
++ if (signal_handlers == handler) {
++ signal_handlers = handler->next();
++ }
++ handler->Unlink();
++ remove = true;
++ signal = handler->signal();
++ } else {
++ any_remaining = true;
++ }
++ }
++ SignalInfo* next = handler->next();
++ if (remove) {
++ delete handler;
++ }
++ handler = next;
++ }
++ if ((signal != -1) && !any_remaining) {
++ struct sigaction act = {};
++ act.sa_handler = oldact_handler;
++ VOID_NO_RETRY_EXPECTED(sigaction(signal, &act, NULL));
++ }
++}
++
++void ProcessInfoList::Init() {
++ active_processes_ = NULL;
++ ASSERT(ProcessInfoList::mutex_ == nullptr);
++ ProcessInfoList::mutex_ = new Mutex();
++}
++
++void ProcessInfoList::Cleanup() {
++ ASSERT(ProcessInfoList::mutex_ != nullptr);
++ delete ProcessInfoList::mutex_;
++ ProcessInfoList::mutex_ = nullptr;
++}
++
++void ExitCodeHandler::Init() {
++ running_ = false;
++ process_count_ = 0;
++ terminate_done_ = false;
++ ASSERT(ExitCodeHandler::monitor_ == nullptr);
++ ExitCodeHandler::monitor_ = new Monitor();
++}
++
++void ExitCodeHandler::Cleanup() {
++ ASSERT(ExitCodeHandler::monitor_ != nullptr);
++ delete ExitCodeHandler::monitor_;
++ ExitCodeHandler::monitor_ = nullptr;
++}
++
++void Process::Init() {
++ ExitCodeHandler::Init();
++ ProcessInfoList::Init();
++
++ ASSERT(signal_mutex == nullptr);
++ signal_mutex = new Mutex();
++ signal_handlers = NULL;
++
++ ASSERT(Process::global_exit_code_mutex_ == nullptr);
++ Process::global_exit_code_mutex_ = new Mutex();
++}
++
++void Process::Cleanup() {
++ ClearAllSignalHandlers();
++
++ ASSERT(signal_mutex != nullptr);
++ delete signal_mutex;
++ signal_mutex = nullptr;
++
++ ASSERT(Process::global_exit_code_mutex_ != nullptr);
++ delete Process::global_exit_code_mutex_;
++ Process::global_exit_code_mutex_ = nullptr;
++
++ ProcessInfoList::Cleanup();
++ ExitCodeHandler::Cleanup();
++}
++
++} // namespace bin
++} // namespace dart
++
++#endif // defined(DART_HOST_OS_OHOS)
+diff --git a/runtime/bin/security_context_ohos.cc b/runtime/bin/security_context_ohos.cc
+new file mode 100644
+index 00000000000..5d801a5b07b
+--- /dev/null
++++ b/runtime/bin/security_context_ohos.cc
+@@ -0,0 +1,69 @@
++// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
++// for details. All rights reserved. Use of this source code is governed by a
++// BSD-style license that can be found in the LICENSE file.
++
++#if !defined(DART_IO_SECURE_SOCKET_DISABLED)
++
++#include "platform/globals.h"
++#if defined(DART_HOST_OS_OHOS)
++
++#include "bin/security_context.h"
++
++#include
++#include
++#include
++
++#include "bin/directory.h"
++#include "bin/file.h"
++#include "bin/secure_socket_filter.h"
++#include "bin/secure_socket_utils.h"
++#include "platform/syslog.h"
++
++namespace dart {
++namespace bin {
++
++// The security context won't necessarily use the compiled-in root certificates,
++// but since there is no way to update the size of the allocation after creating
++// the weak persistent handle, we assume that it will. Note that when the
++// root certs aren't compiled in, |root_certificates_pem_length| is 0.
++const intptr_t SSLCertContext::kApproximateSize =
++ sizeof(SSLCertContext) + root_certificates_pem_length;
++
++void SSLCertContext::TrustBuiltinRoots() {
++ if (root_certs_file() != NULL) {
++ LoadRootCertFile(root_certs_file());
++ return;
++ }
++ if (root_certs_cache() != NULL) {
++ LoadRootCertCache(root_certs_cache());
++ return;
++ }
++
++ const char* bundle = "/etc/ssl/certs/cacert.pem";
++ const char* cachedir = "/etc/ssl/certs";
++ if (File::Exists(NULL, bundle)) {
++ LoadRootCertFile(bundle);
++ return;
++ }
++
++ if (Directory::Exists(NULL, cachedir) == Directory::EXISTS) {
++ LoadRootCertCache(cachedir);
++ return;
++ }
++}
++
++void SSLCertContext::RegisterCallbacks(SSL* ssl) {
++ // No callbacks to register for implementations using BoringSSL's built-in
++ // verification mechanism.
++}
++
++TrustEvaluateHandlerFunc SSLCertContext::GetTrustEvaluateHandler() const {
++ return nullptr;
++}
++
++} // namespace bin
++} // namespace dart
++
++#endif // defined(DART_HOST_OS_OHOS)
++
++#endif // !defined(DART_IO_SECURE_SOCKET_DISABLED)
+diff --git a/runtime/bin/socket.cc b/runtime/bin/socket.cc
+index f61a7372148..adb7f1dc25e 100644
+--- a/runtime/bin/socket.cc
++++ b/runtime/bin/socket.cc
+@@ -209,12 +209,12 @@ Dart_Handle ListeningSocketRegistry::CreateUnixDomainBindListen(
+ return result;
+ }
+
+-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS)
+ // Abstract unix domain socket doesn't exist in file system.
+ if (File::Exists(namespc, addr.un.sun_path) && path[0] != '@') {
+ #else
+ if (File::Exists(namespc, addr.un.sun_path)) {
+-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS)
+ if (unix_domain_sockets_ != nullptr) {
+ // If there is a socket listening on this file. Ensure
+ // that it was created with `shared` mode and current `shared`
+@@ -286,7 +286,7 @@ bool ListeningSocketRegistry::CloseOneSafe(OSSocket* os_socket,
+ }
+ // Unlink the socket file, if os_socket contains unix domain sockets.
+ if (os_socket->address.addr.sa_family == AF_UNIX) {
+-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS)
+ // If the socket is abstract, which has a path starting with a null byte,
+ // unlink() is not necessary because the file doesn't exist.
+ if (os_socket->address.un.sun_path[0] != '\0') {
+@@ -294,7 +294,7 @@ bool ListeningSocketRegistry::CloseOneSafe(OSSocket* os_socket,
+ }
+ #else
+ Utils::Unlink(os_socket->address.un.sun_path);
+-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS)
+ // Remove os_socket from unix_domain_sockets_ list.
+ OSSocket* prev = nullptr;
+ OSSocket* current = unix_domain_sockets_;
+diff --git a/runtime/bin/socket.h b/runtime/bin/socket.h
+index 4da25e5b6a5..8592fcd7a77 100644
+--- a/runtime/bin/socket.h
++++ b/runtime/bin/socket.h
+@@ -262,7 +262,7 @@ class ListeningSocketRegistry {
+ const char* path) {
+ while (current != nullptr) {
+ ASSERT(current->address.addr.sa_family == AF_UNIX);
+-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS)
+ bool condition;
+ if (path[0] == '\0') {
+ condition = current->address.un.sun_path[0] == '\0' &&
+@@ -280,7 +280,7 @@ class ListeningSocketRegistry {
+ namespc, path) == File::kIdentical) {
+ return current;
+ }
+-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS)
+ current = current->next;
+ }
+ return nullptr;
+diff --git a/runtime/bin/socket_base.cc b/runtime/bin/socket_base.cc
+index 40357d0afaf..087d14aa615 100644
+--- a/runtime/bin/socket_base.cc
++++ b/runtime/bin/socket_base.cc
+@@ -130,7 +130,7 @@ void SocketAddress::GetSockAddr(Dart_Handle obj, RawAddr* addr) {
+ Dart_Handle SocketAddress::GetUnixDomainSockAddr(const char* path,
+ Namespace* namespc,
+ RawAddr* addr) {
+-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS)
+ NamespaceScope ns(namespc, path);
+ path = ns.path();
+ bool is_abstract = (path[0] == '@');
+@@ -141,7 +141,7 @@ Dart_Handle SocketAddress::GetUnixDomainSockAddr(const char* path,
+ // connection will be rejected.
+ bzero(addr->un.sun_path, sizeof(addr->un.sun_path));
+ }
+-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS)
+ if (sizeof(path) > sizeof(addr->un.sun_path)) {
+ OSError os_error(-1,
+ "The length of path exceeds the limit. "
+@@ -151,12 +151,12 @@ Dart_Handle SocketAddress::GetUnixDomainSockAddr(const char* path,
+ }
+ addr->un.sun_family = AF_UNIX;
+ Utils::SNPrint(addr->un.sun_path, sizeof(addr->un.sun_path), "%s", path);
+-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS)
+ // In case of abstract namespace, transfer the leading '@' into a null byte.
+ if (is_abstract) {
+ addr->un.sun_path[0] = '\0';
+ }
+-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS)
+ return Dart_Null();
+ }
+
+diff --git a/runtime/bin/socket_base.h b/runtime/bin/socket_base.h
+index a72a8b6dcd4..99775010f12 100644
+--- a/runtime/bin/socket_base.h
++++ b/runtime/bin/socket_base.h
+@@ -9,7 +9,7 @@
+ // Declare the OS-specific types ahead of defining the generic class.
+ #if defined(DART_HOST_OS_FUCHSIA)
+ #include "bin/socket_base_fuchsia.h"
+-#elif defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
++#elif defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS)
+ #include "bin/socket_base_linux.h"
+ #elif defined(DART_HOST_OS_MACOS)
+ #include "bin/socket_base_macos.h"
+@@ -85,7 +85,7 @@ class SocketAddress {
+
+ private:
+ #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
+- defined(DART_HOST_OS_ANDROID)
++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS)
+ // Unix domain address is only on Linux, Mac OS and Android now.
+ // unix(7) require sun_path to be 108 bytes on Linux and Android, 104 bytes on
+ // Mac OS.
+@@ -95,7 +95,7 @@ class SocketAddress {
+ #else
+ char as_string_[INET6_ADDRSTRLEN];
+ #endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
+- // defined(DART_HOST_OS_ANDROID)
++ // defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS)
+ RawAddr addr_;
+
+ DISALLOW_COPY_AND_ASSIGN(SocketAddress);
+diff --git a/runtime/bin/socket_base_linux.cc b/runtime/bin/socket_base_linux.cc
+index 570f03b23d2..f20c49c0d1a 100644
+--- a/runtime/bin/socket_base_linux.cc
++++ b/runtime/bin/socket_base_linux.cc
+@@ -3,7 +3,7 @@
+ // BSD-style license that can be found in the LICENSE file.
+
+ #include "platform/globals.h"
+-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS)
+
+ #include "bin/socket_base.h"
+
+diff --git a/runtime/bin/socket_base_posix.cc b/runtime/bin/socket_base_posix.cc
+index 3838e6d11ee..e10127aa71a 100644
+--- a/runtime/bin/socket_base_posix.cc
++++ b/runtime/bin/socket_base_posix.cc
+@@ -3,8 +3,7 @@
+ // BSD-style license that can be found in the LICENSE file.
+
+ #include "platform/globals.h"
+-#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || \
+- defined(DART_HOST_OS_MACOS)
++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS)
+ #include "bin/socket_base.h"
+
+ #include // NOLINT
+@@ -509,4 +508,4 @@ bool SocketBase::SetOption(intptr_t fd,
+ } // namespace dart
+
+ #endif // defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || \
+- // defined(DART_HOST_OS_MACOS)
++ // defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS)
+diff --git a/runtime/bin/socket_ohos.cc b/runtime/bin/socket_ohos.cc
+new file mode 100644
+index 00000000000..0d4386b14c6
+--- /dev/null
++++ b/runtime/bin/socket_ohos.cc
+@@ -0,0 +1,280 @@
++// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
++// for details. All rights reserved. Use of this source code is governed by a
++// BSD-style license that can be found in the LICENSE file.
++
++#include "platform/globals.h"
++#if defined(DART_HOST_OS_OHOS)
++
++#include "bin/socket.h"
++
++#include // NOLINT
++
++#include "bin/fdutils.h"
++#include "platform/signal_blocker.h"
++#include "platform/syslog.h"
++#include "platform/utils.h"
++
++namespace dart {
++namespace bin {
++
++Socket::Socket(intptr_t fd)
++ : ReferenceCounted(),
++ fd_(fd),
++ isolate_port_(Dart_GetMainPortId()),
++ port_(ILLEGAL_PORT),
++ udp_receive_buffer_(NULL) {}
++
++void Socket::CloseFd() {
++ SetClosedFd();
++}
++
++void Socket::SetClosedFd() {
++ fd_ = kClosedFd;
++}
++
++static intptr_t Create(const RawAddr& addr) {
++ intptr_t fd;
++ intptr_t type = SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC;
++ fd = NO_RETRY_EXPECTED(socket(addr.ss.ss_family, type, 0));
++ if (fd < 0) {
++ return -1;
++ }
++ return fd;
++}
++
++static intptr_t Connect(intptr_t fd, const RawAddr& addr) {
++ intptr_t result = TEMP_FAILURE_RETRY(
++ connect(fd, &addr.addr, SocketAddress::GetAddrLength(addr)));
++ if ((result == 0) || (errno == EINPROGRESS)) {
++ return fd;
++ }
++ FDUtils::SaveErrorAndClose(fd);
++ return -1;
++}
++
++intptr_t Socket::CreateConnect(const RawAddr& addr) {
++ intptr_t fd = Create(addr);
++ if (fd < 0) {
++ return fd;
++ }
++ return Connect(fd, addr);
++}
++
++intptr_t Socket::CreateUnixDomainConnect(const RawAddr& addr) {
++ intptr_t fd = Create(addr);
++ if (fd < 0) {
++ return fd;
++ }
++ intptr_t result = TEMP_FAILURE_RETRY(connect(
++ fd, (struct sockaddr*)&addr.un, SocketAddress::GetAddrLength(addr)));
++ if (result == 0 || errno == EAGAIN) {
++ return fd;
++ }
++ FDUtils::SaveErrorAndClose(fd);
++ return -1;
++}
++
++intptr_t Socket::CreateBindConnect(const RawAddr& addr,
++ const RawAddr& source_addr) {
++ intptr_t fd = Create(addr);
++ if (fd < 0) {
++ return fd;
++ }
++
++ intptr_t result = TEMP_FAILURE_RETRY(
++ bind(fd, &source_addr.addr, SocketAddress::GetAddrLength(source_addr)));
++ if (result != 0) {
++ FDUtils::SaveErrorAndClose(fd);
++ return -1;
++ }
++
++ return Connect(fd, addr);
++}
++
++intptr_t Socket::CreateUnixDomainBindConnect(const RawAddr& addr,
++ const RawAddr& source_addr) {
++ intptr_t fd = Create(addr);
++ if (fd < 0) {
++ return fd;
++ }
++
++ intptr_t result = TEMP_FAILURE_RETRY(
++ bind(fd, &source_addr.addr, SocketAddress::GetAddrLength(source_addr)));
++ if (result != 0) {
++ FDUtils::SaveErrorAndClose(fd);
++ return -1;
++ }
++
++ result = TEMP_FAILURE_RETRY(connect(fd, (struct sockaddr*)&addr.un,
++ SocketAddress::GetAddrLength(addr)));
++ if (result == 0 || errno == EAGAIN) {
++ return fd;
++ }
++ FDUtils::SaveErrorAndClose(fd);
++ return -1;
++}
++
++intptr_t Socket::CreateBindDatagram(const RawAddr& addr,
++ bool reuseAddress,
++ bool reusePort,
++ int ttl) {
++ intptr_t fd;
++
++ fd = NO_RETRY_EXPECTED(socket(addr.addr.sa_family,
++ SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
++ IPPROTO_UDP));
++ if (fd < 0) {
++ return -1;
++ }
++
++ if (reuseAddress) {
++ int optval = 1;
++ VOID_NO_RETRY_EXPECTED(
++ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)));
++ }
++
++ if (reusePort) {
++#ifdef SO_REUSEPORT // Not all Linux versions support this.
++ int optval = 1;
++ int reuse_port_success =
++ setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
++ // Even if it's defined, we might be running on a kernel
++ // that doesn't support it at runtime.
++ if (reuse_port_success != 0) {
++ if (errno == EINTR) {
++ FATAL("Unexpected EINTR errno");
++ }
++ const int kBufferSize = 1024;
++ char error_buf[kBufferSize];
++ Syslog::PrintErr("Dart Socket ERROR: %s:%d: %s.", __FILE__, __LINE__,
++ Utils::StrError(errno, error_buf, kBufferSize));
++ }
++#else // !defined SO_REUSEPORT
++ Syslog::PrintErr(
++ "Dart Socket ERROR: %s:%d: `reusePort` not available on this Linux "
++ "version.",
++ __FILE__, __LINE__);
++#endif // SO_REUSEPORT
++ }
++
++ if (!SocketBase::SetMulticastHops(fd,
++ addr.addr.sa_family == AF_INET
++ ? SocketAddress::TYPE_IPV4
++ : SocketAddress::TYPE_IPV6,
++ ttl)) {
++ FDUtils::SaveErrorAndClose(fd);
++ return -1;
++ }
++
++ if (NO_RETRY_EXPECTED(
++ bind(fd, &addr.addr, SocketAddress::GetAddrLength(addr))) < 0) {
++ FDUtils::SaveErrorAndClose(fd);
++ return -1;
++ }
++ return fd;
++}
++
++intptr_t ServerSocket::CreateBindListen(const RawAddr& addr,
++ intptr_t backlog,
++ bool v6_only) {
++ intptr_t fd;
++
++ fd = NO_RETRY_EXPECTED(
++ socket(addr.ss.ss_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
++ if (fd < 0) {
++ return -1;
++ }
++
++ int optval = 1;
++ VOID_NO_RETRY_EXPECTED(
++ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)));
++
++ if (addr.ss.ss_family == AF_INET6) {
++ optval = v6_only ? 1 : 0;
++ VOID_NO_RETRY_EXPECTED(
++ setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval)));
++ }
++
++ if (NO_RETRY_EXPECTED(
++ bind(fd, &addr.addr, SocketAddress::GetAddrLength(addr))) < 0) {
++ FDUtils::SaveErrorAndClose(fd);
++ return -1;
++ }
++
++ // Test for invalid socket port 65535 (some browsers disallow it).
++ if ((SocketAddress::GetAddrPort(addr) == 0) &&
++ (SocketBase::GetPort(fd) == 65535)) {
++ // Don't close the socket until we have created a new socket, ensuring
++ // that we do not get the bad port number again.
++ intptr_t new_fd = CreateBindListen(addr, backlog, v6_only);
++ FDUtils::SaveErrorAndClose(fd);
++ return new_fd;
++ }
++
++ if (NO_RETRY_EXPECTED(listen(fd, backlog > 0 ? backlog : SOMAXCONN)) != 0) {
++ FDUtils::SaveErrorAndClose(fd);
++ return -1;
++ }
++
++ return fd;
++}
++
++intptr_t ServerSocket::CreateUnixDomainBindListen(const RawAddr& addr,
++ intptr_t backlog) {
++ intptr_t fd = Create(addr);
++ if (NO_RETRY_EXPECTED(bind(fd, (struct sockaddr*)&addr.un,
++ SocketAddress::GetAddrLength(addr))) < 0) {
++ FDUtils::SaveErrorAndClose(fd);
++ return -1;
++ }
++ if (NO_RETRY_EXPECTED(listen(fd, backlog > 0 ? backlog : SOMAXCONN)) != 0) {
++ FDUtils::SaveErrorAndClose(fd);
++ return -1;
++ }
++ return fd;
++}
++
++bool ServerSocket::StartAccept(intptr_t fd) {
++ USE(fd);
++ return true;
++}
++
++static bool IsTemporaryAcceptError(int error) {
++ // On Linux a number of protocol errors should be treated as EAGAIN.
++ // These are the ones for TCP/IP.
++ return (error == EAGAIN) || (error == ENETDOWN) || (error == EPROTO) ||
++ (error == ENOPROTOOPT) || (error == EHOSTDOWN) || (error == ENONET) ||
++ (error == EHOSTUNREACH) || (error == EOPNOTSUPP) ||
++ (error == ENETUNREACH);
++}
++
++intptr_t ServerSocket::Accept(intptr_t fd) {
++ intptr_t socket;
++ struct sockaddr clientaddr;
++ socklen_t addrlen = sizeof(clientaddr);
++ socket = TEMP_FAILURE_RETRY(accept(fd, &clientaddr, &addrlen));
++ if (socket == -1) {
++ if (IsTemporaryAcceptError(errno)) {
++ // We need to signal to the caller that this is actually not an
++ // error. We got woken up from the poll on the listening socket,
++ // but there is no connection ready to be accepted.
++ ASSERT(kTemporaryFailure != -1);
++ socket = kTemporaryFailure;
++ }
++ } else {
++ if (!FDUtils::SetCloseOnExec(socket)) {
++ FDUtils::SaveErrorAndClose(socket);
++ return -1;
++ }
++ if (!FDUtils::SetNonBlocking(socket)) {
++ FDUtils::SaveErrorAndClose(socket);
++ return -1;
++ }
++ }
++ return socket;
++}
++
++} // namespace bin
++} // namespace dart
++
++#endif // defined(DART_HOST_OS_OHOS)
+diff --git a/runtime/bin/stdio_ohos.cc b/runtime/bin/stdio_ohos.cc
+new file mode 100644
+index 00000000000..8dd67b67b45
+--- /dev/null
++++ b/runtime/bin/stdio_ohos.cc
+@@ -0,0 +1,139 @@
++// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
++// for details. All rights reserved. Use of this source code is governed by a
++// BSD-style license that can be found in the LICENSE file.
++
++#include "platform/globals.h"
++#if defined(DART_HOST_OS_OHOS)
++
++#include "bin/stdio.h"
++
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++
++#include "bin/fdutils.h"
++#include "platform/signal_blocker.h"
++
++namespace dart {
++namespace bin {
++
++bool Stdin::ReadByte(intptr_t fd, int* byte) {
++ unsigned char b;
++ ssize_t s = TEMP_FAILURE_RETRY(read(fd, &b, 1));
++ if (s < 0) {
++ return false;
++ }
++ *byte = (s == 0) ? -1 : b;
++ return true;
++}
++
++bool Stdin::GetEchoMode(intptr_t fd, bool* enabled) {
++ struct termios term;
++ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term));
++ if (status != 0) {
++ return false;
++ }
++ *enabled = ((term.c_lflag & ECHO) != 0);
++ return true;
++}
++
++bool Stdin::SetEchoMode(intptr_t fd, bool enabled) {
++ struct termios term;
++ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term));
++ if (status != 0) {
++ return false;
++ }
++ if (enabled) {
++ term.c_lflag |= ECHO;
++ } else {
++ term.c_lflag &= ~(ECHO);
++ }
++ status = NO_RETRY_EXPECTED(tcsetattr(fd, TCSANOW, &term));
++ return (status == 0);
++}
++
++bool Stdin::GetEchoNewlineMode(intptr_t fd, bool* enabled) {
++ struct termios term;
++ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term));
++ if (status != 0) {
++ return false;
++ }
++ *enabled = ((term.c_lflag & ECHONL) != 0);
++ return true;
++}
++
++bool Stdin::SetEchoNewlineMode(intptr_t fd, bool enabled) {
++ struct termios term;
++ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term));
++ if (status != 0) {
++ return false;
++ }
++ if (enabled) {
++ term.c_lflag |= ECHONL;
++ } else {
++ term.c_lflag &= ~(ECHONL);
++ }
++ status = NO_RETRY_EXPECTED(tcsetattr(fd, TCSANOW, &term));
++ return (status == 0);
++}
++
++bool Stdin::GetLineMode(intptr_t fd, bool* enabled) {
++ struct termios term;
++ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term));
++ if (status != 0) {
++ return false;
++ }
++ *enabled = ((term.c_lflag & ICANON) != 0);
++ return true;
++}
++
++bool Stdin::SetLineMode(intptr_t fd, bool enabled) {
++ struct termios term;
++ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term));
++ if (status != 0) {
++ return false;
++ }
++ if (enabled) {
++ term.c_lflag |= ICANON;
++ } else {
++ term.c_lflag &= ~(ICANON);
++ }
++ status = NO_RETRY_EXPECTED(tcsetattr(fd, TCSANOW, &term));
++ return (status == 0);
++}
++
++static bool TermIsKnownToSupportAnsi() {
++ const char* term = getenv("TERM");
++ if (term == NULL) {
++ return false;
++ }
++
++ return strstr(term, "xterm") != NULL || strstr(term, "screen") != NULL ||
++ strstr(term, "rxvt") != NULL;
++}
++
++bool Stdin::AnsiSupported(intptr_t fd, bool* supported) {
++ *supported = (isatty(fd) != 0) && TermIsKnownToSupportAnsi();
++ return true;
++}
++
++bool Stdout::GetTerminalSize(intptr_t fd, int size[2]) {
++ struct winsize w;
++ int status = NO_RETRY_EXPECTED(ioctl(fd, TIOCGWINSZ, &w));
++ if ((status == 0) && ((w.ws_col != 0) || (w.ws_row != 0))) {
++ size[0] = w.ws_col;
++ size[1] = w.ws_row;
++ return true;
++ }
++ return false;
++}
++
++bool Stdout::AnsiSupported(intptr_t fd, bool* supported) {
++ *supported = (isatty(fd) != 0) && TermIsKnownToSupportAnsi();
++ return true;
++}
++
++} // namespace bin
++} // namespace dart
++
++#endif // defined(DART_HOST_OS_OHOS)
+diff --git a/runtime/bin/sync_socket_ohos.cc b/runtime/bin/sync_socket_ohos.cc
+new file mode 100644
+index 00000000000..be15707ba28
+--- /dev/null
++++ b/runtime/bin/sync_socket_ohos.cc
+@@ -0,0 +1,92 @@
++// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
++// for details. All rights reserved. Use of this source code is governed by a
++// BSD-style license that can be found in the LICENSE file.
++
++#include "platform/globals.h"
++#if defined(DART_HOST_OS_OHOS)
++
++#include "bin/sync_socket.h"
++
++#include // NOLINT
++
++#include "bin/fdutils.h"
++#include "bin/socket_base.h"
++#include "platform/signal_blocker.h"
++
++namespace dart {
++namespace bin {
++
++bool SynchronousSocket::Initialize() {
++ // Nothing to do on Ohos.
++ return true;
++}
++
++static intptr_t Create(const RawAddr& addr) {
++ intptr_t fd;
++ intptr_t type = SOCK_STREAM | SOCK_CLOEXEC;
++ fd = NO_RETRY_EXPECTED(socket(addr.ss.ss_family, type, 0));
++ if (fd < 0) {
++ return -1;
++ }
++ return fd;
++}
++
++static intptr_t Connect(intptr_t fd, const RawAddr& addr) {
++ intptr_t result = TEMP_FAILURE_RETRY(
++ connect(fd, &addr.addr, SocketAddress::GetAddrLength(addr)));
++ if (result == 0) {
++ return fd;
++ }
++ ASSERT(errno != EINPROGRESS);
++ FDUtils::SaveErrorAndClose(fd);
++ return -1;
++}
++
++intptr_t SynchronousSocket::CreateConnect(const RawAddr& addr) {
++ intptr_t fd = Create(addr);
++ if (fd < 0) {
++ return fd;
++ }
++ return Connect(fd, addr);
++}
++
++intptr_t SynchronousSocket::Available(intptr_t fd) {
++ return SocketBase::Available(fd);
++}
++
++intptr_t SynchronousSocket::GetPort(intptr_t fd) {
++ return SocketBase::GetPort(fd);
++}
++
++SocketAddress* SynchronousSocket::GetRemotePeer(intptr_t fd, intptr_t* port) {
++ return SocketBase::GetRemotePeer(fd, port);
++}
++
++intptr_t SynchronousSocket::Read(intptr_t fd,
++ void* buffer,
++ intptr_t num_bytes) {
++ return SocketBase::Read(fd, buffer, num_bytes, SocketBase::kSync);
++}
++
++intptr_t SynchronousSocket::Write(intptr_t fd,
++ const void* buffer,
++ intptr_t num_bytes) {
++ return SocketBase::Write(fd, buffer, num_bytes, SocketBase::kSync);
++}
++
++void SynchronousSocket::ShutdownRead(intptr_t fd) {
++ VOID_NO_RETRY_EXPECTED(shutdown(fd, SHUT_RD));
++}
++
++void SynchronousSocket::ShutdownWrite(intptr_t fd) {
++ VOID_NO_RETRY_EXPECTED(shutdown(fd, SHUT_WR));
++}
++
++void SynchronousSocket::Close(intptr_t fd) {
++ return SocketBase::Close(fd);
++}
++
++} // namespace bin
++} // namespace dart
++
++#endif // defined(DART_HOST_OS_OHOS)
+diff --git a/runtime/bin/thread.h b/runtime/bin/thread.h
+index e41e8b620a5..c509f3daf17 100644
+--- a/runtime/bin/thread.h
++++ b/runtime/bin/thread.h
+@@ -22,6 +22,8 @@ class Monitor;
+ #include "bin/thread_fuchsia.h"
+ #elif defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
+ #include "bin/thread_linux.h"
++#elif defined(DART_HOST_OS_OHOS)
++#include "bin/thread_ohos.h"
+ #elif defined(DART_HOST_OS_MACOS)
+ #include "bin/thread_macos.h"
+ #elif defined(DART_HOST_OS_WINDOWS)
+diff --git a/runtime/bin/thread_absl.cc b/runtime/bin/thread_absl.cc
+index 23bc5e55a5c..3bb3084b6c6 100644
+--- a/runtime/bin/thread_absl.cc
++++ b/runtime/bin/thread_absl.cc
+@@ -71,7 +71,7 @@ static void* ThreadStart(void* data_ptr) {
+ uword parameter = data->parameter();
+ delete data;
+
+-#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX)
++#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS)
+ // Set the thread name. There is 16 bytes limit on the name (including \0).
+ // pthread_setname_np ignores names that are too long rather than truncating.
+ char truncated_name[16];
+diff --git a/runtime/bin/thread_ohos.cc b/runtime/bin/thread_ohos.cc
+new file mode 100644
+index 00000000000..33657a4f6c5
+--- /dev/null
++++ b/runtime/bin/thread_ohos.cc
+@@ -0,0 +1,280 @@
++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
++// for details. All rights reserved. Use of this source code is governed by a
++// BSD-style license that can be found in the LICENSE file.
++
++#include "platform/globals.h"
++#if defined(DART_HOST_OS_OHOS) && !defined(DART_USE_ABSL)
++
++#include "bin/thread.h"
++#include "bin/thread_ohos.h"
++
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++
++#include "platform/assert.h"
++#include "platform/utils.h"
++
++namespace dart {
++namespace bin {
++
++#define VALIDATE_PTHREAD_RESULT(result) \
++ if (result != 0) { \
++ const int kBufferSize = 1024; \
++ char error_buf[kBufferSize]; \
++ FATAL("pthread error: %d (%s)", result, \
++ Utils::StrError(result, error_buf, kBufferSize)); \
++ }
++
++#ifdef DEBUG
++#define RETURN_ON_PTHREAD_FAILURE(result) \
++ if (result != 0) { \
++ const int kBufferSize = 1024; \
++ char error_buf[kBufferSize]; \
++ fprintf(stderr, "%s:%d: pthread error: %d (%s)\n", __FILE__, __LINE__, \
++ result, Utils::StrError(result, error_buf, kBufferSize)); \
++ return result; \
++ }
++#else
++#define RETURN_ON_PTHREAD_FAILURE(result) \
++ if (result != 0) { \
++ return result; \
++ }
++#endif
++
++static void ComputeTimeSpecMicros(struct timespec* ts, int64_t micros) {
++ int64_t secs = micros / kMicrosecondsPerSecond;
++ int64_t nanos =
++ (micros - (secs * kMicrosecondsPerSecond)) * kNanosecondsPerMicrosecond;
++ int result = clock_gettime(CLOCK_MONOTONIC, ts);
++ ASSERT(result == 0);
++ ts->tv_sec += secs;
++ ts->tv_nsec += nanos;
++ if (ts->tv_nsec >= kNanosecondsPerSecond) {
++ ts->tv_sec += 1;
++ ts->tv_nsec -= kNanosecondsPerSecond;
++ }
++}
++
++class ThreadStartData {
++ public:
++ ThreadStartData(const char* name,
++ Thread::ThreadStartFunction function,
++ uword parameter)
++ : name_(name), function_(function), parameter_(parameter) {}
++
++ const char* name() const { return name_; }
++ Thread::ThreadStartFunction function() const { return function_; }
++ uword parameter() const { return parameter_; }
++
++ private:
++ const char* name_;
++ Thread::ThreadStartFunction function_;
++ uword parameter_;
++
++ DISALLOW_COPY_AND_ASSIGN(ThreadStartData);
++};
++
++// Dispatch to the thread start function provided by the caller. This trampoline
++// is used to ensure that the thread is properly destroyed if the thread just
++// exits.
++static void* ThreadStart(void* data_ptr) {
++ ThreadStartData* data = reinterpret_cast(data_ptr);
++
++ const char* name = data->name();
++ Thread::ThreadStartFunction function = data->function();
++ uword parameter = data->parameter();
++ delete data;
++
++ // Set the thread name. There is 16 bytes limit on the name (including \0).
++ // pthread_setname_np ignores names that are too long rather than truncating.
++ char truncated_name[16];
++ snprintf(truncated_name, sizeof(truncated_name), "%s", name);
++ pthread_setname_np(pthread_self(), truncated_name);
++
++ // Call the supplied thread start function handing it its parameters.
++ function(parameter);
++
++ return NULL;
++}
++
++int Thread::Start(const char* name,
++ ThreadStartFunction function,
++ uword parameter) {
++ pthread_attr_t attr;
++ int result = pthread_attr_init(&attr);
++ RETURN_ON_PTHREAD_FAILURE(result);
++
++ result = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
++ RETURN_ON_PTHREAD_FAILURE(result);
++
++ result = pthread_attr_setstacksize(&attr, Thread::GetMaxStackSize());
++ RETURN_ON_PTHREAD_FAILURE(result);
++
++ ThreadStartData* data = new ThreadStartData(name, function, parameter);
++
++ pthread_t tid;
++ result = pthread_create(&tid, &attr, ThreadStart, data);
++ RETURN_ON_PTHREAD_FAILURE(result);
++
++ result = pthread_attr_destroy(&attr);
++ RETURN_ON_PTHREAD_FAILURE(result);
++
++ return 0;
++}
++
++const ThreadId Thread::kInvalidThreadId = static_cast(0);
++
++intptr_t Thread::GetMaxStackSize() {
++ const int kStackSize = (128 * kWordSize * KB);
++ return kStackSize;
++}
++
++ThreadId Thread::GetCurrentThreadId() {
++ return pthread_self();
++}
++
++bool Thread::Compare(ThreadId a, ThreadId b) {
++ return (pthread_equal(a, b) != 0);
++}
++
++Mutex::Mutex() {
++ pthread_mutexattr_t attr;
++ int result = pthread_mutexattr_init(&attr);
++ VALIDATE_PTHREAD_RESULT(result);
++
++#if defined(DEBUG)
++ result = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
++ VALIDATE_PTHREAD_RESULT(result);
++#endif // defined(DEBUG)
++
++ result = pthread_mutex_init(data_.mutex(), &attr);
++ // Verify that creating a pthread_mutex succeeded.
++ VALIDATE_PTHREAD_RESULT(result);
++
++ result = pthread_mutexattr_destroy(&attr);
++ VALIDATE_PTHREAD_RESULT(result);
++}
++
++Mutex::~Mutex() {
++ int result = pthread_mutex_destroy(data_.mutex());
++ // Verify that the pthread_mutex was destroyed.
++ VALIDATE_PTHREAD_RESULT(result);
++}
++
++void Mutex::Lock() {
++ int result = pthread_mutex_lock(data_.mutex());
++ // Specifically check for dead lock to help debugging.
++ ASSERT(result != EDEADLK);
++ ASSERT(result == 0); // Verify no other errors.
++ // TODO(iposva): Do we need to track lock owners?
++}
++
++bool Mutex::TryLock() {
++ int result = pthread_mutex_trylock(data_.mutex());
++ // Return false if the lock is busy and locking failed.
++ if (result == EBUSY) {
++ return false;
++ }
++ ASSERT(result == 0); // Verify no other errors.
++ // TODO(iposva): Do we need to track lock owners?
++ return true;
++}
++
++void Mutex::Unlock() {
++ // TODO(iposva): Do we need to track lock owners?
++ int result = pthread_mutex_unlock(data_.mutex());
++ // Specifically check for wrong thread unlocking to aid debugging.
++ ASSERT(result != EPERM);
++ ASSERT(result == 0); // Verify no other errors.
++}
++
++Monitor::Monitor() {
++ pthread_mutexattr_t mutex_attr;
++ int result = pthread_mutexattr_init(&mutex_attr);
++ VALIDATE_PTHREAD_RESULT(result);
++
++#if defined(DEBUG)
++ result = pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_ERRORCHECK);
++ VALIDATE_PTHREAD_RESULT(result);
++#endif // defined(DEBUG)
++
++ result = pthread_mutex_init(data_.mutex(), &mutex_attr);
++ VALIDATE_PTHREAD_RESULT(result);
++
++ result = pthread_mutexattr_destroy(&mutex_attr);
++ VALIDATE_PTHREAD_RESULT(result);
++
++ pthread_condattr_t cond_attr;
++ result = pthread_condattr_init(&cond_attr);
++ VALIDATE_PTHREAD_RESULT(result);
++
++ result = pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC);
++ VALIDATE_PTHREAD_RESULT(result);
++
++ result = pthread_cond_init(data_.cond(), &cond_attr);
++ VALIDATE_PTHREAD_RESULT(result);
++
++ result = pthread_condattr_destroy(&cond_attr);
++ VALIDATE_PTHREAD_RESULT(result);
++}
++
++Monitor::~Monitor() {
++ int result = pthread_mutex_destroy(data_.mutex());
++ VALIDATE_PTHREAD_RESULT(result);
++
++ result = pthread_cond_destroy(data_.cond());
++ VALIDATE_PTHREAD_RESULT(result);
++}
++
++void Monitor::Enter() {
++ int result = pthread_mutex_lock(data_.mutex());
++ VALIDATE_PTHREAD_RESULT(result);
++ // TODO(iposva): Do we need to track lock owners?
++}
++
++void Monitor::Exit() {
++ // TODO(iposva): Do we need to track lock owners?
++ int result = pthread_mutex_unlock(data_.mutex());
++ VALIDATE_PTHREAD_RESULT(result);
++}
++
++Monitor::WaitResult Monitor::Wait(int64_t millis) {
++ return WaitMicros(millis * kMicrosecondsPerMillisecond);
++}
++
++Monitor::WaitResult Monitor::WaitMicros(int64_t micros) {
++ // TODO(iposva): Do we need to track lock owners?
++ Monitor::WaitResult retval = kNotified;
++ if (micros == kNoTimeout) {
++ // Wait forever.
++ int result = pthread_cond_wait(data_.cond(), data_.mutex());
++ VALIDATE_PTHREAD_RESULT(result);
++ } else {
++ struct timespec ts;
++ ComputeTimeSpecMicros(&ts, micros);
++ int result = pthread_cond_timedwait(data_.cond(), data_.mutex(), &ts);
++ ASSERT((result == 0) || (result == ETIMEDOUT));
++ if (result == ETIMEDOUT) {
++ retval = kTimedOut;
++ }
++ }
++ return retval;
++}
++
++void Monitor::Notify() {
++ // TODO(iposva): Do we need to track lock owners?
++ int result = pthread_cond_signal(data_.cond());
++ VALIDATE_PTHREAD_RESULT(result);
++}
++
++void Monitor::NotifyAll() {
++ // TODO(iposva): Do we need to track lock owners?
++ int result = pthread_cond_broadcast(data_.cond());
++ VALIDATE_PTHREAD_RESULT(result);
++}
++
++} // namespace bin
++} // namespace dart
++
++#endif // defined(DART_HOST_OS_OHOS) && !defined(DART_USE_ABSL)
+diff --git a/runtime/bin/thread_ohos.h b/runtime/bin/thread_ohos.h
+new file mode 100644
+index 00000000000..c15799d45f7
+--- /dev/null
++++ b/runtime/bin/thread_ohos.h
+@@ -0,0 +1,57 @@
++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
++// for details. All rights reserved. Use of this source code is governed by a
++// BSD-style license that can be found in the LICENSE file.
++
++#ifndef RUNTIME_BIN_THREAD_OHOS_H_
++#define RUNTIME_BIN_THREAD_OHOS_H_
++
++#if !defined(RUNTIME_BIN_THREAD_H_)
++#error Do not include thread_ohos.h directly; use thread.h instead.
++#endif
++
++#include
++
++#include "platform/assert.h"
++#include "platform/globals.h"
++
++namespace dart {
++namespace bin {
++
++typedef pthread_t ThreadId;
++
++class MutexData {
++ private:
++ MutexData() {}
++ ~MutexData() {}
++
++ pthread_mutex_t* mutex() { return &mutex_; }
++
++ pthread_mutex_t mutex_;
++
++ friend class Mutex;
++
++ DISALLOW_ALLOCATION();
++ DISALLOW_COPY_AND_ASSIGN(MutexData);
++};
++
++class MonitorData {
++ private:
++ MonitorData() {}
++ ~MonitorData() {}
++
++ pthread_mutex_t* mutex() { return &mutex_; }
++ pthread_cond_t* cond() { return &cond_; }
++
++ pthread_mutex_t mutex_;
++ pthread_cond_t cond_;
++
++ friend class Monitor;
++
++ DISALLOW_ALLOCATION();
++ DISALLOW_COPY_AND_ASSIGN(MonitorData);
++};
++
++} // namespace bin
++} // namespace dart
++
++#endif // RUNTIME_BIN_THREAD_OHOS_H_
+diff --git a/runtime/bin/utils_ohos.cc b/runtime/bin/utils_ohos.cc
+new file mode 100644
+index 00000000000..487e4f30199
+--- /dev/null
++++ b/runtime/bin/utils_ohos.cc
+@@ -0,0 +1,113 @@
++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
++// for details. All rights reserved. Use of this source code is governed by a
++// BSD-style license that can be found in the LICENSE file.
++
++#include "platform/globals.h"
++#if defined(DART_HOST_OS_OHOS)
++
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++
++#include "bin/utils.h"
++#include "platform/assert.h"
++#include "platform/utils.h"
++
++namespace dart {
++namespace bin {
++
++OSError::OSError() : sub_system_(kSystem), code_(0), message_(NULL) {
++ Reload();
++}
++
++void OSError::Reload() {
++ SetCodeAndMessage(kSystem, errno);
++}
++
++void OSError::SetCodeAndMessage(SubSystem sub_system, int code) {
++ set_sub_system(sub_system);
++ set_code(code);
++ if (sub_system == kSystem) {
++ const int kBufferSize = 1024;
++ char error_buf[kBufferSize];
++ SetMessage(Utils::StrError(code, error_buf, kBufferSize));
++ } else if (sub_system == kGetAddressInfo) {
++ SetMessage(gai_strerror(code));
++ } else {
++ UNREACHABLE();
++ }
++}
++
++const char* StringUtils::ConsoleStringToUtf8(const char* str,
++ intptr_t len,
++ intptr_t* result_len) {
++ return NULL;
++}
++
++const char* StringUtils::Utf8ToConsoleString(const char* utf8,
++ intptr_t len,
++ intptr_t* result_len) {
++ return NULL;
++}
++
++char* StringUtils::ConsoleStringToUtf8(char* str,
++ intptr_t len,
++ intptr_t* result_len) {
++ return NULL;
++}
++
++char* StringUtils::Utf8ToConsoleString(char* utf8,
++ intptr_t len,
++ intptr_t* result_len) {
++ return NULL;
++}
++
++bool ShellUtils::GetUtf8Argv(int argc, char** argv) {
++ return false;
++}
++
++void TimerUtils::InitOnce() {}
++
++int64_t TimerUtils::GetCurrentMonotonicMillis() {
++ return GetCurrentMonotonicMicros() / 1000;
++}
++
++int64_t TimerUtils::GetCurrentMonotonicMicros() {
++ struct timespec ts;
++ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
++ UNREACHABLE();
++ return 0;
++ }
++ // Convert to microseconds.
++ int64_t result = ts.tv_sec;
++ result *= kMicrosecondsPerSecond;
++ result += (ts.tv_nsec / kNanosecondsPerMicrosecond);
++ return result;
++}
++
++void TimerUtils::Sleep(int64_t millis) {
++ struct timespec req; // requested.
++ struct timespec rem; // remainder.
++ int64_t micros = millis * kMicrosecondsPerMillisecond;
++ int64_t seconds = micros / kMicrosecondsPerSecond;
++ micros = micros - seconds * kMicrosecondsPerSecond;
++ int64_t nanos = micros * kNanosecondsPerMicrosecond;
++ req.tv_sec = seconds;
++ req.tv_nsec = nanos;
++ while (true) {
++ int r = nanosleep(&req, &rem);
++ if (r == 0) {
++ break;
++ }
++ // We should only ever see an interrupt error.
++ ASSERT(errno == EINTR);
++ // Copy remainder into requested and repeat.
++ req = rem;
++ }
++}
++
++} // namespace bin
++} // namespace dart
++
++#endif // defined(DART_HOST_OS_OHOS)
+diff --git a/runtime/bin/virtual_memory_posix.cc b/runtime/bin/virtual_memory_posix.cc
+index b7f0eaf872c..5a86a3cd5d1 100644
+--- a/runtime/bin/virtual_memory_posix.cc
++++ b/runtime/bin/virtual_memory_posix.cc
+@@ -4,7 +4,7 @@
+
+ #include "platform/globals.h"
+ #if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || \
+- defined(DART_HOST_OS_MACOS)
++ defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS)
+
+ #include "bin/virtual_memory.h"
+
+@@ -50,6 +50,12 @@ VirtualMemory* VirtualMemory::Allocate(intptr_t size,
+ }
+ #endif // defined(DART_HOST_OS_MACOS)
+
++#if defined(DART_HOST_OS_OHOS)
++ if (is_executable) {
++ map_flags |= MAP_JIT_OHOS;
++ }
++#endif // defined(DART_HOST_OS_OHOS)
++
+ // Some 64-bit microarchitectures store only the low 32-bits of targets as
+ // part of indirect branch prediction, predicting that the target's upper bits
+ // will be same as the call instruction's address. This leads to misprediction
+diff --git a/runtime/bin/vmservice_impl.cc b/runtime/bin/vmservice_impl.cc
+index 7b7559b8af6..f58bb029064 100644
+--- a/runtime/bin/vmservice_impl.cc
++++ b/runtime/bin/vmservice_impl.cc
+@@ -132,6 +132,9 @@ bool VmService::Setup(const char* server_ip,
+
+ Dart_Handle result;
+
++ //TODO 临时关闭验证
++ auth_codes_disabled=true;
++
+ // Prepare builtin and its dependent libraries for use to resolve URIs.
+ // Set up various closures, e.g: printing, timers etc.
+ // Set up 'package root' for URI resolution.
+diff --git a/runtime/lib/ffi_dynamic_library.cc b/runtime/lib/ffi_dynamic_library.cc
+index 2c6df520f71..cd9840afcae 100644
+--- a/runtime/lib/ffi_dynamic_library.cc
++++ b/runtime/lib/ffi_dynamic_library.cc
+@@ -23,7 +23,7 @@
+ #include "vm/zone_text_buffer.h"
+
+ #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
+- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA)
++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS)
+ #include
+ #endif
+
+@@ -175,7 +175,7 @@ DEFINE_NATIVE_ENTRY(Ffi_dl_open, 0, 1) {
+
+ DEFINE_NATIVE_ENTRY(Ffi_dl_processLibrary, 0, 0) {
+ #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
+- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA)
++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS)
+ return DynamicLibrary::New(RTLD_DEFAULT, false);
+ #else
+ return DynamicLibrary::New(kWindowsDynamicLibraryProcessPtr, false);
+@@ -425,7 +425,7 @@ static void* FfiResolveAsset(Thread* const thread,
+ handle = LoadDynamicLibrary(path.ToCString(), error);
+ } else if (asset_type.Equals(Symbols::process())) {
+ #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
+- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA)
++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS)
+ handle = RTLD_DEFAULT;
+ #else
+ handle = kWindowsDynamicLibraryProcessPtr;
+diff --git a/runtime/platform/globals.h b/runtime/platform/globals.h
+index 29bbb969d1d..5c1d340af81 100644
+--- a/runtime/platform/globals.h
++++ b/runtime/platform/globals.h
+@@ -109,6 +109,11 @@
+ // Check for Android first, to determine its difference from Linux.
+ #define DART_HOST_OS_ANDROID 1
+
++#elif defined(DART_TARGET_OS_OHOS) && defined(DART_RUNTIME_OS_OHOS)
++
++#define DART_HOST_OS_OHOS 1
++#define MAP_JIT_OHOS 0x1000
++
+ #elif defined(__linux__) || defined(__FreeBSD__)
+
+ // Generic Linux.
+@@ -384,7 +389,8 @@ struct simd128_value_t {
+
+ #if !defined(DART_TARGET_OS_ANDROID) && !defined(DART_TARGET_OS_FUCHSIA) && \
+ !defined(DART_TARGET_OS_MACOS_IOS) && !defined(DART_TARGET_OS_LINUX) && \
+- !defined(DART_TARGET_OS_MACOS) && !defined(DART_TARGET_OS_WINDOWS)
++ !defined(DART_TARGET_OS_MACOS) && !defined(DART_TARGET_OS_WINDOWS) && \
++ !defined(DART_TARGET_OS_OHOS)
+ // No target OS specified; pick the one matching the host OS.
+ #if defined(DART_HOST_OS_ANDROID)
+ #define DART_TARGET_OS_ANDROID 1
+@@ -395,6 +401,8 @@ struct simd128_value_t {
+ #define DART_TARGET_OS_MACOS_IOS 1
+ #elif defined(DART_HOST_OS_LINUX)
+ #define DART_TARGET_OS_LINUX 1
++#elif defined(DART_HOST_OS_OHOS)
++#define DART_TARGET_OS_OHOS 1
+ #elif defined(DART_HOST_OS_MACOS)
+ #define DART_TARGET_OS_MACOS 1
+ #elif defined(DART_HOST_OS_WINDOWS)
+@@ -741,6 +749,8 @@ DART_FORCE_INLINE D bit_copy(const S& source) {
+ #define kHostOperatingSystemName "macos"
+ #elif defined(DART_HOST_OS_WINDOWS)
+ #define kHostOperatingSystemName "windows"
++#elif defined(DART_HOST_OS_OHOS)
++#define kHostOperatingSystemName "ohos"
+ #else
+ #error Host operating system detection failed.
+ #endif
+@@ -785,6 +795,8 @@ DART_FORCE_INLINE D bit_copy(const S& source) {
+ #define kTargetOperatingSystemName "android"
+ #elif defined(DART_TARGET_OS_FUCHSIA)
+ #define kTargetOperatingSystemName "fuchsia"
++#elif defined(DART_TARGET_OS_OHOS)
++#define kTargetOperatingSystemName "ohos"
+ #elif defined(DART_TARGET_OS_LINUX)
+ #define kTargetOperatingSystemName "linux"
+ #elif defined(DART_TARGET_OS_MACOS_IOS)
+diff --git a/runtime/platform/platform_sources.gni b/runtime/platform/platform_sources.gni
+index e300478c7ea..2ed478e2ee3 100644
+--- a/runtime/platform/platform_sources.gni
++++ b/runtime/platform/platform_sources.gni
+@@ -25,6 +25,7 @@ platform_sources = [
+ "syslog_android.cc",
+ "syslog_fuchsia.cc",
+ "syslog_linux.cc",
++ "syslog_ohos.cc",
+ "syslog_macos.cc",
+ "syslog_win.cc",
+ "text_buffer.cc",
+@@ -40,6 +41,7 @@ platform_sources = [
+ "utils_android.cc",
+ "utils_fuchsia.cc",
+ "utils_linux.cc",
++ "utils_ohos.cc",
+ "utils_macos.cc",
+ "utils_win.cc",
+ ]
+diff --git a/runtime/platform/syslog_ohos.cc b/runtime/platform/syslog_ohos.cc
+new file mode 100644
+index 00000000000..139881f7271
+--- /dev/null
++++ b/runtime/platform/syslog_ohos.cc
+@@ -0,0 +1,46 @@
++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
++// for details. All rights reserved. Use of this source code is governed by a
++// BSD-style license that can be found in the LICENSE file.
++
++#include "platform/globals.h"
++#if defined(DART_HOST_OS_OHOS)
++
++#include "platform/syslog.h"
++
++#include // NOLINT
++#include // NOLINT
++
++namespace dart {
++#define HILOG_LOG_DOMAIN 0x0000
++#define HILOG_LOG_TAG "Dart"
++
++void Syslog::VPrint(const char* format, va_list args) {
++
++ va_list stdio_args;
++ va_copy(stdio_args, args);
++ vprintf(format, stdio_args);
++ fflush(stdout);
++ va_end(stdio_args);
++
++ va_list log_args;
++ va_copy(log_args, args);
++ ((void)OH_LOG_Print(LOG_APP, LOG_INFO, HILOG_LOG_DOMAIN, \
++ HILOG_LOG_TAG, format, log_args));
++ va_end(log_args);
++}
++
++void Syslog::VPrintErr(const char* format, va_list args) {
++ va_list stdio_args;
++ va_copy(stdio_args, args);
++ vprintf(format, stdio_args);
++ fflush(stdout);
++ va_end(stdio_args);
++ va_list log_args;
++ va_copy(log_args, args);
++ ((void)OH_LOG_Print(LOG_APP, LOG_ERROR, HILOG_LOG_DOMAIN, \
++ HILOG_LOG_TAG, format, log_args));
++ va_end(log_args);
++}
++
++} // namespace dart
++#endif // defined(DART_HOST_OS_OHOS)
+diff --git a/runtime/platform/utils.cc b/runtime/platform/utils.cc
+index e7179ed8040..88e016482c1 100644
+--- a/runtime/platform/utils.cc
++++ b/runtime/platform/utils.cc
+@@ -8,7 +8,7 @@
+ #include "platform/globals.h"
+
+ #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
+- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA)
++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS)
+ #include
+ #endif
+
+@@ -262,7 +262,7 @@ static void GetLastErrorAsString(char** error) {
+ if (error == nullptr) return; // Nothing to do.
+
+ #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
+- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA)
++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS)
+ const char* status = dlerror();
+ *error = status != nullptr ? strdup(status) : nullptr;
+ #elif defined(DART_HOST_OS_WINDOWS)
+@@ -294,7 +294,7 @@ void* Utils::LoadDynamicLibrary(const char* library_path, char** error) {
+ void* handle = nullptr;
+
+ #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
+- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA)
++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS)
+ handle = dlopen(library_path, RTLD_LAZY);
+ #elif defined(DART_HOST_OS_WINDOWS)
+ SetLastError(0); // Clear any errors.
+@@ -329,7 +329,7 @@ void* Utils::ResolveSymbolInDynamicLibrary(void* library_handle,
+ void* result = nullptr;
+
+ #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
+- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA)
++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS)
+ dlerror(); // Clear any errors.
+ result = dlsym(library_handle, symbol);
+ // Note: nullptr might be a valid return from dlsym. Must call dlerror
+@@ -353,7 +353,7 @@ void Utils::UnloadDynamicLibrary(void* library_handle, char** error) {
+ bool ok = false;
+
+ #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
+- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA)
++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS)
+ ok = dlclose(library_handle) == 0;
+ #elif defined(DART_HOST_OS_WINDOWS)
+ SetLastError(0); // Clear any errors.
+diff --git a/runtime/platform/utils.h b/runtime/platform/utils.h
+index 18e83926d7c..250b29a0fb4 100644
+--- a/runtime/platform/utils.h
++++ b/runtime/platform/utils.h
+@@ -686,6 +686,8 @@ class Utils {
+ #include "platform/utils_fuchsia.h"
+ #elif defined(DART_HOST_OS_LINUX)
+ #include "platform/utils_linux.h"
++#elif defined(DART_HOST_OS_OHOS)
++#include "platform/utils_ohos.h"
+ #elif defined(DART_HOST_OS_MACOS)
+ #include "platform/utils_macos.h"
+ #elif defined(DART_HOST_OS_WINDOWS)
+diff --git a/runtime/platform/utils_ohos.cc b/runtime/platform/utils_ohos.cc
+new file mode 100644
+index 00000000000..3c768b4e9e7
+--- /dev/null
++++ b/runtime/platform/utils_ohos.cc
+@@ -0,0 +1,54 @@
++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
++// for details. All rights reserved. Use of this source code is governed by a
++// BSD-style license that can be found in the LICENSE file.
++
++#include "platform/globals.h"
++#if defined(DART_HOST_OS_OHOS)
++
++#include "platform/utils.h"
++#include "platform/utils_ohos.h"
++
++namespace dart {
++
++char* Utils::StrNDup(const char* s, intptr_t n) {
++ return strndup(s, n);
++}
++
++char* Utils::StrDup(const char* s) {
++ return strdup(s);
++}
++
++intptr_t Utils::StrNLen(const char* s, intptr_t n) {
++ return strnlen(s, n);
++}
++
++int Utils::SNPrint(char* str, size_t size, const char* format, ...) {
++ va_list args;
++ va_start(args, format);
++ int retval = VSNPrint(str, size, format, args);
++ va_end(args);
++ return retval;
++}
++
++int Utils::VSNPrint(char* str, size_t size, const char* format, va_list args) {
++ MSAN_UNPOISON(str, size);
++ int retval = vsnprintf(str, size, format, args);
++ if (retval < 0) {
++ FATAL("Fatal error in Utils::VSNPrint with format '%s'", format);
++ }
++ return retval;
++}
++
++int Utils::Close(int fildes) {
++ return close(fildes);
++}
++size_t Utils::Read(int filedes, void* buf, size_t nbyte) {
++ return read(filedes, buf, nbyte);
++}
++int Utils::Unlink(const char* path) {
++ return unlink(path);
++}
++
++} // namespace dart
++
++#endif // defined(DART_HOST_OS_OHOS)
+diff --git a/runtime/platform/utils_ohos.h b/runtime/platform/utils_ohos.h
+new file mode 100644
+index 00000000000..abb486ed2d4
+--- /dev/null
++++ b/runtime/platform/utils_ohos.h
+@@ -0,0 +1,56 @@
++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
++// for details. All rights reserved. Use of this source code is governed by a
++// BSD-style license that can be found in the LICENSE file.
++
++#ifndef RUNTIME_PLATFORM_UTILS_OHOS_H_
++#define RUNTIME_PLATFORM_UTILS_OHOS_H_
++
++#if !defined(RUNTIME_PLATFORM_UTILS_H_)
++#error Do not include utils_ohos.h directly; use utils.h instead.
++#endif
++
++#include // NOLINT
++
++namespace dart {
++
++inline uint16_t Utils::HostToBigEndian16(uint16_t value) {
++ return htobe16(value);
++}
++
++inline uint32_t Utils::HostToBigEndian32(uint32_t value) {
++ return htobe32(value);
++}
++
++inline uint64_t Utils::HostToBigEndian64(uint64_t value) {
++ return htobe64(value);
++}
++
++inline uint16_t Utils::HostToLittleEndian16(uint16_t value) {
++ return htole16(value);
++}
++
++inline uint32_t Utils::HostToLittleEndian32(uint32_t value) {
++ return htole32(value);
++}
++
++inline uint64_t Utils::HostToLittleEndian64(uint64_t value) {
++ return htole64(value);
++}
++
++inline char* Utils::StrError(int err, char* buffer, size_t bufsize) {
++#if !defined(__GLIBC__) || \
++ ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !_GNU_SOURCE)
++ // Use the XSI version.
++ if (strerror_r(err, buffer, bufsize) != 0) {
++ snprintf(buffer, bufsize, "%s", "strerror_r failed");
++ }
++ return buffer;
++#else
++ // Use the GNU specific version.
++ return strerror_r(err, buffer, bufsize);
++#endif
++}
++
++} // namespace dart
++
++#endif // RUNTIME_PLATFORM_UTILS_OHOS_H_
+diff --git a/runtime/runtime_args.gni b/runtime/runtime_args.gni
+index dc10b71b7d6..6a43ccad027 100644
+--- a/runtime/runtime_args.gni
++++ b/runtime/runtime_args.gni
+@@ -80,7 +80,7 @@ declare_args() {
+ # The analyze_snapshot tool is only supported on 64 bit AOT builds that use
+ # ELF.
+ build_analyze_snapshot =
+- (target_os == "linux" || target_os == "android" ||
++ (target_os == "linux" || target_os == "android" || target_os == "ohos" ||
+ target_os == "fuchsia") &&
+ (dart_target_arch == "x64" || dart_target_arch == "arm64" ||
+ dart_target_arch == "riscv64")
+diff --git a/runtime/vm/compiler/ffi/abi.cc b/runtime/vm/compiler/ffi/abi.cc
+index 66fa5a8d564..243aca8daf3 100644
+--- a/runtime/vm/compiler/ffi/abi.cc
++++ b/runtime/vm/compiler/ffi/abi.cc
+@@ -31,7 +31,7 @@ static_assert(offsetof(AbiAlignmentUint64, i) == 8,
+ "FFI transformation alignment");
+ #elif (defined(HOST_ARCH_IA32) && /* NOLINT(whitespace/parens) */ \
+ (defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
+- defined(DART_HOST_OS_ANDROID))) || \
++ defined(DART_HOST_OS_ANDROID)) || defined(DART_HOST_OS_OHOS)) || \
+ (defined(HOST_ARCH_ARM) && defined(DART_HOST_OS_IOS))
+ static_assert(offsetof(AbiAlignmentDouble, d) == 4,
+ "FFI transformation alignment");
+@@ -53,6 +53,8 @@ static_assert(offsetof(AbiAlignmentUint64, i) == 8,
+ #define DART_TARGET_OS_NAME Fuchsia
+ #elif defined(DART_TARGET_OS_LINUX)
+ #define DART_TARGET_OS_NAME Linux
++#elif defined(DART_TARGET_OS_OHOS)
++#define DART_TARGET_OS_NAME Ohos
+ #elif defined(DART_TARGET_OS_MACOS)
+ #if DART_TARGET_OS_MACOS_IOS
+ #define DART_TARGET_OS_NAME IOS
+diff --git a/runtime/vm/compiler/ffi/abi.h b/runtime/vm/compiler/ffi/abi.h
+index ff917bf55d1..e5161488782 100644
+--- a/runtime/vm/compiler/ffi/abi.h
++++ b/runtime/vm/compiler/ffi/abi.h
+@@ -25,6 +25,8 @@ enum class Abi {
+ kAndroidIA32,
+ kAndroidX64,
+ kAndroidRiscv64,
++ kOhosArm,
++ kOhosArm64,
+ kFuchsiaArm64,
+ kFuchsiaX64,
+ kFuchsiaRiscv64,
+@@ -51,9 +53,9 @@ const int64_t num_abis = static_cast(Abi::kWindowsX64) + 1;
+ // - runtime/vm/compiler/frontend/kernel_to_il.cc
+ static_assert(static_cast(Abi::kAndroidArm) == 0,
+ "Enum value unexpected.");
+-static_assert(static_cast(Abi::kWindowsX64) == 21,
++static_assert(static_cast(Abi::kWindowsX64) == 23,
+ "Enum value unexpected.");
+-static_assert(num_abis == 22, "Enum value unexpected.");
++static_assert(num_abis == 24, "Enum value unexpected.");
+
+ // The target ABI. Defines sizes and alignment of native types.
+ Abi TargetAbi();
+diff --git a/runtime/vm/compiler/ffi/native_calling_convention.cc b/runtime/vm/compiler/ffi/native_calling_convention.cc
+index 3914afe326d..b6da03881fb 100644
+--- a/runtime/vm/compiler/ffi/native_calling_convention.cc
++++ b/runtime/vm/compiler/ffi/native_calling_convention.cc
+@@ -37,7 +37,7 @@ static bool SoftFpAbi(bool has_varargs, bool is_result) {
+ }
+ #else // !defined(FFI_UNIT_TESTS)
+ static bool SoftFpAbi(bool has_varargs, bool is_result) {
+-#if defined(TARGET_ARCH_ARM) && defined(DART_TARGET_OS_ANDROID)
++#if defined(TARGET_ARCH_ARM) && (defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_OHOS))
+ return true;
+ #elif defined(TARGET_ARCH_ARM)
+ return has_varargs;
+diff --git a/runtime/vm/compiler/ffi/native_calling_convention_test.cc b/runtime/vm/compiler/ffi/native_calling_convention_test.cc
+index 456a6936465..664642d1f5f 100644
+--- a/runtime/vm/compiler/ffi/native_calling_convention_test.cc
++++ b/runtime/vm/compiler/ffi/native_calling_convention_test.cc
+@@ -665,7 +665,7 @@ UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_regress46127) {
+ RunSignatureTest(Z, "regress46127", arguments, struct_type);
+
+ #if defined(TARGET_ARCH_IA32) && \
+- (defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_LINUX))
++ (defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_LINUX)) || defined(DART_TARGET_OS_OHOS)
+ // We must count the result pointer passed on the stack as well.
+ EXPECT_EQ(4, native_calling_convention.StackTopInBytes());
+ #else
+diff --git a/runtime/vm/cpu_arm.cc b/runtime/vm/cpu_arm.cc
+index d40147b2ea0..454ac3e4ff0 100644
+--- a/runtime/vm/cpu_arm.cc
++++ b/runtime/vm/cpu_arm.cc
+@@ -54,7 +54,7 @@ DEFINE_FLAG(bool,
+ "Use integer division instruction if supported");
+
+ #if defined(TARGET_HOST_MISMATCH)
+-#if defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_MACOS_IOS)
++#if defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_MACOS_IOS) || defined(DART_TARGET_OS_OHOS)
+ DEFINE_FLAG(bool, sim_use_hardfp, false, "Use the hardfp ABI.");
+ #else
+ DEFINE_FLAG(bool, sim_use_hardfp, true, "Use the hardfp ABI.");
+@@ -80,7 +80,7 @@ void CPU::FlushICache(uword start, uword size) {
+ // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/sys_icache_invalidate.3.html
+ #if defined(DART_HOST_OS_IOS)
+ sys_icache_invalidate(reinterpret_cast(start), size);
+-#elif defined(DART_HOST_OS_LINUX)
++#elif defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS)
+ char* beg = reinterpret_cast(start);
+ char* end = reinterpret_cast(start + size);
+ __builtin___clear_cache(beg, end);
+diff --git a/runtime/vm/cpu_arm64.cc b/runtime/vm/cpu_arm64.cc
+index b64a2ee9259..b195a8e82e3 100644
+--- a/runtime/vm/cpu_arm64.cc
++++ b/runtime/vm/cpu_arm64.cc
+@@ -40,7 +40,7 @@ void CPU::FlushICache(uword start, uword size) {
+ // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/sys_icache_invalidate.3.html
+ #if defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_IOS)
+ sys_icache_invalidate(reinterpret_cast(start), size);
+-#elif defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX)
++#elif defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS)
+ char* beg = reinterpret_cast(start);
+ char* end = reinterpret_cast(start + size);
+ __builtin___clear_cache(beg, end);
+diff --git a/runtime/vm/cpu_riscv.cc b/runtime/vm/cpu_riscv.cc
+index e1440a9d2ee..ddabbc4773b 100644
+--- a/runtime/vm/cpu_riscv.cc
++++ b/runtime/vm/cpu_riscv.cc
+@@ -36,7 +36,7 @@ void CPU::FlushICache(uword start, uword size) {
+
+ #if defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_IOS)
+ sys_icache_invalidate(reinterpret_cast(start), size);
+-#elif defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX)
++#elif defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS)
+ char* beg = reinterpret_cast(start);
+ char* end = reinterpret_cast(start + size);
+ __builtin___clear_cache(beg, end);
+diff --git a/runtime/vm/cpuinfo_ohos.cc b/runtime/vm/cpuinfo_ohos.cc
+new file mode 100644
+index 00000000000..e5d90ac5dfb
+--- /dev/null
++++ b/runtime/vm/cpuinfo_ohos.cc
+@@ -0,0 +1,107 @@
++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
++// for details. All rights reserved. Use of this source code is governed by a
++// BSD-style license that can be found in the LICENSE file.
++
++#include "vm/globals.h"
++#if defined(DART_HOST_OS_OHOS)
++
++#include "vm/cpuid.h"
++#include "vm/cpuinfo.h"
++#include "vm/proccpuinfo.h"
++
++#include "platform/assert.h"
++
++// As with Windows, on IA32 and X64, we use the cpuid instruction.
++// The analogous instruction is privileged on ARM, so we resort to
++// reading from /proc/cpuinfo.
++
++namespace dart {
++
++CpuInfoMethod CpuInfo::method_ = kCpuInfoDefault;
++const char* CpuInfo::fields_[kCpuInfoMax] = {0};
++
++void CpuInfo::Init() {
++#if defined(HOST_ARCH_IA32) || defined(HOST_ARCH_X64)
++ fields_[kCpuInfoProcessor] = "vendor_id";
++ fields_[kCpuInfoModel] = "model name";
++ fields_[kCpuInfoHardware] = "model name";
++ fields_[kCpuInfoFeatures] = "flags";
++ fields_[kCpuInfoArchitecture] = "CPU architecture";
++ method_ = kCpuInfoCpuId;
++ CpuId::Init();
++#elif defined(HOST_ARCH_ARM)
++ fields_[kCpuInfoProcessor] = "Processor";
++ fields_[kCpuInfoModel] = "model name";
++ fields_[kCpuInfoHardware] = "Hardware";
++ fields_[kCpuInfoFeatures] = "Features";
++ fields_[kCpuInfoArchitecture] = "CPU architecture";
++ method_ = kCpuInfoSystem;
++ ProcCpuInfo::Init();
++#elif defined(HOST_ARCH_ARM64)
++ fields_[kCpuInfoProcessor] = "Processor";
++ fields_[kCpuInfoModel] = "CPU implementer";
++ fields_[kCpuInfoHardware] = "CPU implementer";
++ fields_[kCpuInfoFeatures] = "Features";
++ fields_[kCpuInfoArchitecture] = "CPU architecture";
++ method_ = kCpuInfoSystem;
++ ProcCpuInfo::Init();
++#elif defined(HOST_ARCH_RISCV32) || defined(HOST_ARCH_RISCV64)
++ // We only rely on the base Linux configuration of IMAFDC, so don't need
++ // dynamic feature detection.
++ method_ = kCpuInfoNone;
++#else
++#error Unrecognized target architecture
++#endif
++}
++
++void CpuInfo::Cleanup() {
++ if (method_ == kCpuInfoCpuId) {
++ CpuId::Cleanup();
++ } else if (method_ == kCpuInfoSystem) {
++ ProcCpuInfo::Cleanup();
++ } else {
++ ASSERT(method_ == kCpuInfoNone);
++ }
++}
++
++bool CpuInfo::FieldContains(CpuInfoIndices idx, const char* search_string) {
++ if (method_ == kCpuInfoCpuId) {
++ const char* field = CpuId::field(idx);
++ bool contains = (strstr(field, search_string) != NULL);
++ free(const_cast(field));
++ return contains;
++ } else if (method_ == kCpuInfoSystem) {
++ return ProcCpuInfo::FieldContains(FieldName(idx), search_string);
++ } else {
++ UNREACHABLE();
++ }
++}
++
++const char* CpuInfo::ExtractField(CpuInfoIndices idx) {
++ if (method_ == kCpuInfoCpuId) {
++ return CpuId::field(idx);
++ } else if (method_ == kCpuInfoSystem) {
++ return ProcCpuInfo::ExtractField(FieldName(idx));
++ } else {
++ UNREACHABLE();
++ }
++}
++
++bool CpuInfo::HasField(const char* field) {
++ if (method_ == kCpuInfoCpuId) {
++ return (strcmp(field, fields_[kCpuInfoProcessor]) == 0) ||
++ (strcmp(field, fields_[kCpuInfoModel]) == 0) ||
++ (strcmp(field, fields_[kCpuInfoHardware]) == 0) ||
++ (strcmp(field, fields_[kCpuInfoFeatures]) == 0);
++ } else if (method_ == kCpuInfoSystem) {
++ return ProcCpuInfo::HasField(field);
++ } else if (method_ == kCpuInfoNone) {
++ return false;
++ } else {
++ UNREACHABLE();
++ }
++}
++
++} // namespace dart
++
++#endif // defined(DART_HOST_OS_OHOS)
+diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc
+index 7fc13a1de68..bb2d7eb8250 100644
+--- a/runtime/vm/dart.cc
++++ b/runtime/vm/dart.cc
+@@ -1071,6 +1071,8 @@ char* Dart::FeaturesString(IsolateGroup* isolate_group,
+ #endif
+ #elif defined(DART_TARGET_OS_LINUX)
+ buffer.AddString(" linux");
++#elif defined(DART_TARGET_OS_OHOS)
++ buffer.AddString(" ohos");
+ #elif defined(DART_TARGET_OS_WINDOWS)
+ buffer.AddString(" windows");
+ #else
+diff --git a/runtime/vm/globals.h b/runtime/vm/globals.h
+index 7cf67f4e416..69c54277b03 100644
+--- a/runtime/vm/globals.h
++++ b/runtime/vm/globals.h
+@@ -116,7 +116,7 @@ const intptr_t kDefaultNewGenSemiMaxSize = (kWordSize <= 4) ? 8 : 16;
+ #if !defined(DART_DISABLE_TIMELINE) && \
+ (defined(DART_ENABLE_TIMELINE) || !defined(PRODUCT) || \
+ defined(DART_HOST_OS_FUCHSIA) || defined(DART_TARGET_OS_FUCHSIA) || \
+- defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_MACOS))
++ defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_OHOS))
+ #define SUPPORT_TIMELINE 1
+ #endif
+
+diff --git a/runtime/vm/image_snapshot.cc b/runtime/vm/image_snapshot.cc
+index b738e456eee..9e2081f4c19 100644
+--- a/runtime/vm/image_snapshot.cc
++++ b/runtime/vm/image_snapshot.cc
+@@ -1060,7 +1060,7 @@ class DwarfAssemblyStream : public DwarfWriteStream {
+ #if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)
+ stream_->WriteString(".section __DWARF,__debug_abbrev,regular,debug\n");
+ #elif defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \
+- defined(DART_TARGET_OS_FUCHSIA)
++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS)
+ stream_->WriteString(".section .debug_abbrev,\"\"\n");
+ #else
+ UNIMPLEMENTED();
+@@ -1070,7 +1070,7 @@ class DwarfAssemblyStream : public DwarfWriteStream {
+ #if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)
+ stream_->WriteString(".section __DWARF,__debug_info,regular,debug\n");
+ #elif defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \
+- defined(DART_TARGET_OS_FUCHSIA)
++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS)
+ stream_->WriteString(".section .debug_info,\"\"\n");
+ #else
+ UNIMPLEMENTED();
+@@ -1082,7 +1082,7 @@ class DwarfAssemblyStream : public DwarfWriteStream {
+ #if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)
+ stream_->WriteString(".section __DWARF,__debug_line,regular,debug\n");
+ #elif defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \
+- defined(DART_TARGET_OS_FUCHSIA)
++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS)
+ stream_->WriteString(".section .debug_line,\"\"\n");
+ #else
+ UNIMPLEMENTED();
+@@ -1170,7 +1170,7 @@ void AssemblyImageWriter::Finalize() {
+ }
+
+ #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \
+- defined(DART_TARGET_OS_FUCHSIA)
++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS)
+ // Non-executable stack.
+ #if defined(TARGET_ARCH_ARM)
+ assembly_stream_->WriteString(".section .note.GNU-stack,\"\",%progbits\n");
+@@ -1403,7 +1403,7 @@ void AssemblyImageWriter::WriteROData(NonStreamingWriteStream* clustered_stream,
+ WriteBytes(bytes + last_position, symbol.offset - last_position);
+ assembly_stream_->Printf("\"%s\":\n", symbol.name);
+ #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \
+- defined(DART_TARGET_OS_FUCHSIA)
++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS)
+ // Output size and type of the read-only data symbol to the assembly stream.
+ assembly_stream_->Printf(".size \"%s\", %zu\n", symbol.name, symbol.size);
+ assembly_stream_->Printf(".type \"%s\", %%object\n", symbol.name);
+@@ -1445,7 +1445,7 @@ bool AssemblyImageWriter::EnterSection(ProgramSection section,
+ current_symbols_ =
+ new (zone_) ZoneGrowableArray(zone_, 0);
+ #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \
+- defined(DART_TARGET_OS_FUCHSIA)
++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS)
+ assembly_stream_->WriteString(".section .rodata\n");
+ #elif defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)
+ assembly_stream_->WriteString(".const\n");
+@@ -1502,7 +1502,7 @@ void AssemblyImageWriter::ExitSection(ProgramSection name,
+ // We should still be in the same section as the last EnterSection.
+ ASSERT_EQUAL(current_section_label_, SectionLabel(name, vm));
+ #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \
+- defined(DART_TARGET_OS_FUCHSIA)
++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS)
+ // Output the size of the section symbol to the assembly stream.
+ assembly_stream_->Printf(".size %s, %zu\n", SectionSymbol(name, vm), size);
+ assembly_stream_->Printf(".type %s, %%object\n", SectionSymbol(name, vm));
+@@ -1597,7 +1597,7 @@ void AssemblyImageWriter::AddCodeSymbol(const Code& code,
+ }
+ assembly_stream_->Printf("\"%s\":\n", symbol);
+ #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \
+- defined(DART_TARGET_OS_FUCHSIA)
++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS)
+ // Output the size of the code symbol to the assembly stream.
+ assembly_stream_->Printf(".size \"%s\", %zu\n", symbol, code.Size());
+ assembly_stream_->Printf(".type \"%s\", %%function\n", symbol);
+diff --git a/runtime/vm/native_symbol_posix.cc b/runtime/vm/native_symbol_posix.cc
+index 458ecbc2929..32edcf8e708 100644
+--- a/runtime/vm/native_symbol_posix.cc
++++ b/runtime/vm/native_symbol_posix.cc
+@@ -4,7 +4,7 @@
+
+ #include "vm/globals.h"
+ #if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || \
+- defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS)
++ defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS)
+
+ #include "platform/memory_sanitizer.h"
+ #include "vm/native_symbol.h"
+diff --git a/runtime/vm/os_ohos.cc b/runtime/vm/os_ohos.cc
+new file mode 100644
+index 00000000000..e28cf5f1b9b
+--- /dev/null
++++ b/runtime/vm/os_ohos.cc
+@@ -0,0 +1,706 @@
++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
++// for details. All rights reserved. Use of this source code is governed by a
++// BSD-style license that can be found in the LICENSE file.
++
++#include "vm/globals.h"
++#if defined(DART_HOST_OS_OHOS)
++
++#include "vm/os.h"
++
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++
++#include "platform/memory_sanitizer.h"
++#include "platform/utils.h"
++#include "vm/code_comments.h"
++#include "vm/code_observers.h"
++#include "vm/dart.h"
++#include "vm/flags.h"
++#include "vm/image_snapshot.h"
++#include "vm/isolate.h"
++#include "vm/lockers.h"
++#include "vm/os_thread.h"
++#include "vm/timeline.h"
++#include "vm/zone.h"
++
++#include // NOLINT
++namespace dart {
++
++// Used to choose between Elf32/Elf64 types based on host archotecture bitsize.
++#if defined(ARCH_IS_64_BIT)
++#define ElfW(Type) Elf64_##Type
++#else
++#define ElfW(Type) Elf32_##Type
++#endif
++
++// Missing from older versions of .
++#if !defined(EM_RISCV)
++#define EM_RISCV 243
++#endif
++
++#ifndef PRODUCT
++
++DEFINE_FLAG(bool,
++ generate_perf_events_symbols,
++ false,
++ "Generate events symbols for profiling with perf (disables dual "
++ "code mapping)");
++
++DEFINE_FLAG(bool,
++ generate_perf_jitdump,
++ false,
++ "Generate jitdump file to use with perf-inject (disables dual code "
++ "mapping)");
++
++DECLARE_FLAG(bool, write_protect_code);
++DECLARE_FLAG(bool, write_protect_vm_isolate);
++#if !defined(DART_PRECOMPILED_RUNTIME)
++DECLARE_FLAG(bool, code_comments);
++#endif
++
++// Linux CodeObservers.
++
++// Simple perf support: generate /tmp/perf-.map file that maps
++// memory ranges to symbol names for JIT generated code. This allows
++// perf-report to resolve addresses falling into JIT generated code.
++// However perf-annotate does not work in this mode because JIT code
++// is transient and does not exist anymore at the moment when you
++// invoke perf-report.
++class PerfCodeObserver : public CodeObserver {
++ public:
++ PerfCodeObserver() : out_file_(NULL) {
++ Dart_FileOpenCallback file_open = Dart::file_open_callback();
++ if (file_open == NULL) {
++ return;
++ }
++ intptr_t pid = getpid();
++ char* filename = OS::SCreate(NULL, "/tmp/perf-%" Pd ".map", pid);
++ out_file_ = (*file_open)(filename, true);
++ free(filename);
++ }
++
++ ~PerfCodeObserver() {
++ Dart_FileCloseCallback file_close = Dart::file_close_callback();
++ if ((file_close == NULL) || (out_file_ == NULL)) {
++ return;
++ }
++ (*file_close)(out_file_);
++ }
++
++ virtual bool IsActive() const {
++ return FLAG_generate_perf_events_symbols && (out_file_ != NULL);
++ }
++
++ virtual void Notify(const char* name,
++ uword base,
++ uword prologue_offset,
++ uword size,
++ bool optimized,
++ const CodeComments* comments) {
++ Dart_FileWriteCallback file_write = Dart::file_write_callback();
++ if ((file_write == NULL) || (out_file_ == NULL)) {
++ return;
++ }
++ const char* marker = optimized ? "*" : "";
++ char* buffer =
++ OS::SCreate(Thread::Current()->zone(), "%" Px " %" Px " %s%s\n", base,
++ size, marker, name);
++ {
++ MutexLocker ml(CodeObservers::mutex());
++ (*file_write)(buffer, strlen(buffer), out_file_);
++ }
++ }
++
++ private:
++ void* out_file_;
++
++ DISALLOW_COPY_AND_ASSIGN(PerfCodeObserver);
++};
++
++// Code observer that generates a JITDUMP[1] file that can be interpreted by
++// perf-inject to generate ELF images for JIT generated code objects, which
++// allows both perf-report and perf-annotate to recognize them.
++//
++// Usage:
++//
++// $ perf record -k mono dart --generate-perf-jitdump benchmark.dart
++// $ perf inject -j -i perf.data -o perf.data.jitted
++// $ perf report -i perf.data.jitted
++//
++// [1] see linux/tools/perf/Documentation/jitdump-specification.txt for
++// JITDUMP binary format.
++class JitDumpCodeObserver : public CodeObserver {
++ public:
++ JitDumpCodeObserver() : pid_(getpid()) {
++ char* const filename = OS::SCreate(nullptr, "/tmp/jit-%" Pd ".dump", pid_);
++ const int fd = open(filename, O_CREAT | O_TRUNC | O_RDWR, 0666);
++ free(filename);
++
++ if (fd == -1) {
++ return;
++ }
++
++ // Map JITDUMP file, this mapping will be recorded by perf. This allows
++ // perf-inject to find this file later.
++ const long page_size = sysconf(_SC_PAGESIZE); // NOLINT(runtime/int)
++ if (page_size == -1) {
++ close(fd);
++ return;
++ }
++
++ mapped_ =
++ mmap(nullptr, page_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
++ if (mapped_ == nullptr) {
++ close(fd);
++ return;
++ }
++ mapped_size_ = page_size;
++
++ out_file_ = fdopen(fd, "w+");
++ if (out_file_ == nullptr) {
++ close(fd);
++ return;
++ }
++
++ // Buffer the output to avoid high IO overheads - we are going to be
++ // writing all JIT generated code out.
++ setvbuf(out_file_, nullptr, _IOFBF, 2 * MB);
++
++ // Disable code write protection and vm isolate write protection, because
++ // calling mprotect on the pages filled with JIT generated code objects
++ // confuses perf.
++ FLAG_write_protect_code = false;
++ FLAG_write_protect_vm_isolate = false;
++
++#if !defined(DART_PRECOMPILED_RUNTIME)
++ // Enable code comments.
++ FLAG_code_comments = true;
++#endif
++
++ // Write JITDUMP header.
++ WriteHeader();
++ }
++
++ ~JitDumpCodeObserver() {
++ if (mapped_ != nullptr) {
++ munmap(mapped_, mapped_size_);
++ mapped_ = nullptr;
++ }
++
++ if (out_file_ != nullptr) {
++ fclose(out_file_);
++ out_file_ = nullptr;
++ }
++ }
++
++ virtual bool IsActive() const {
++ return FLAG_generate_perf_jitdump && (out_file_ != nullptr);
++ }
++
++ virtual void Notify(const char* name,
++ uword base,
++ uword prologue_offset,
++ uword size,
++ bool optimized,
++ const CodeComments* comments) {
++ MutexLocker ml(CodeObservers::mutex());
++
++ const char* marker = optimized ? "*" : "";
++ char* buffer = OS::SCreate(Thread::Current()->zone(), "%s%s", marker, name);
++ const size_t name_length = strlen(buffer);
++
++ WriteDebugInfo(base, comments);
++
++ CodeLoadEvent ev;
++ ev.event = BaseEvent::kLoad;
++ ev.size = sizeof(ev) + (name_length + 1) + size;
++ ev.time_stamp = OS::GetCurrentMonotonicTicks();
++ ev.process_id = getpid();
++ ev.thread_id = syscall(SYS_gettid);
++ ev.vma = base;
++ ev.code_address = base;
++ ev.code_size = size;
++ ev.code_id = code_id_++;
++
++ WriteFully(&ev, sizeof(ev));
++ WriteFully(buffer, name_length + 1);
++ WriteFully(reinterpret_cast(base), size);
++ }
++
++ private:
++ struct Header {
++ const uint32_t magic = 0x4A695444;
++ const uint32_t version = 1;
++ const uint32_t size = sizeof(Header);
++ uint32_t elf_mach_target;
++ const uint32_t reserved = 0xDEADBEEF;
++ uint32_t process_id;
++ uint64_t time_stamp;
++ const uint64_t flags = 0;
++ };
++
++ struct BaseEvent {
++ enum Event {
++ kLoad = 0,
++ kMove = 1,
++ kDebugInfo = 2,
++ kClose = 3,
++ kUnwindingInfo = 4
++ };
++
++ uint32_t event;
++ uint32_t size;
++ uint64_t time_stamp;
++ };
++
++ struct CodeLoadEvent : BaseEvent {
++ uint32_t process_id;
++ uint32_t thread_id;
++ uint64_t vma;
++ uint64_t code_address;
++ uint64_t code_size;
++ uint64_t code_id;
++ };
++
++ struct DebugInfoEvent : BaseEvent {
++ uint64_t address;
++ uint64_t entry_count;
++ // DebugInfoEntry entries[entry_count_];
++ };
++
++ struct DebugInfoEntry {
++ uint64_t address;
++ int32_t line_number;
++ int32_t column;
++ // Followed by nul-terminated name.
++ };
++
++ static uint32_t GetElfMachineArchitecture() {
++#if TARGET_ARCH_IA32
++ return EM_386;
++#elif TARGET_ARCH_X64
++ return EM_X86_64;
++#elif TARGET_ARCH_ARM
++ return EM_ARM;
++#elif TARGET_ARCH_ARM64
++ return EM_AARCH64;
++#elif TARGET_ARCH_RISCV32 || TARGET_ARCH_RISCV64
++ return EM_RISCV;
++#else
++ UNREACHABLE();
++ return 0;
++#endif
++ }
++
++#if ARCH_IS_64_BIT
++ static const int kElfHeaderSize = 0x40;
++#else
++ static const int kElfHeaderSize = 0x34;
++#endif
++
++ void WriteDebugInfo(uword base, const CodeComments* comments) {
++ if (comments == nullptr || comments->Length() == 0) {
++ return;
++ }
++
++ // Open the comments file for the given code object.
++ // Note: for some reason we can't emit all comments into a single file
++ // the mapping between PCs and lines goes out of sync (might be
++ // perf-annotate bug).
++ char* comments_file_name =
++ OS::SCreate(nullptr, "/tmp/jit-%" Pd "-%" Pd ".cmts", pid_, code_id_);
++ const intptr_t filename_length = strlen(comments_file_name);
++ FILE* comments_file = fopen(comments_file_name, "w");
++ setvbuf(comments_file, nullptr, _IOFBF, 2 * MB);
++
++ // Count the number of DebugInfoEntry we are going to emit: one
++ // per PC.
++ intptr_t entry_count = 0;
++ for (uint64_t i = 0, len = comments->Length(); i < len;) {
++ const intptr_t pc_offset = comments->PCOffsetAt(i);
++ while (i < len && comments->PCOffsetAt(i) == pc_offset) {
++ i++;
++ }
++ entry_count++;
++ }
++
++ DebugInfoEvent info;
++ info.event = BaseEvent::kDebugInfo;
++ info.time_stamp = OS::GetCurrentMonotonicTicks();
++ info.address = base;
++ info.entry_count = entry_count;
++ info.size = sizeof(info) +
++ entry_count * (sizeof(DebugInfoEntry) + filename_length + 1);
++ const int32_t padding = Utils::RoundUp(info.size, 8) - info.size;
++ info.size += padding;
++
++ // Write out DebugInfoEvent record followed by entry_count DebugInfoEntry
++ // records.
++ WriteFully(&info, sizeof(info));
++ intptr_t line_number = 0; // Line number within comments_file.
++ for (intptr_t i = 0, len = comments->Length(); i < len;) {
++ const intptr_t pc_offset = comments->PCOffsetAt(i);
++ while (i < len && comments->PCOffsetAt(i) == pc_offset) {
++ line_number += WriteLn(comments_file, comments->CommentAt(i));
++ i++;
++ }
++ DebugInfoEntry entry;
++ entry.address = base + pc_offset + kElfHeaderSize;
++ entry.line_number = line_number;
++ entry.column = 0;
++ WriteFully(&entry, sizeof(entry));
++ WriteFully(comments_file_name, filename_length + 1);
++ }
++
++ // Write out the padding.
++ const char padding_bytes[8] = {0};
++ WriteFully(padding_bytes, padding);
++
++ fclose(comments_file);
++ free(comments_file_name);
++ }
++
++ void WriteHeader() {
++ Header header;
++ header.elf_mach_target = GetElfMachineArchitecture();
++ header.process_id = getpid();
++ header.time_stamp = OS::GetCurrentTimeMicros();
++ WriteFully(&header, sizeof(header));
++ }
++
++ // Returns number of new-lines written.
++ intptr_t WriteLn(FILE* f, const char* comment) {
++ fputs(comment, f);
++ fputc('\n', f);
++
++ intptr_t line_count = 1;
++ while ((comment = strstr(comment, "\n")) != nullptr) {
++ line_count++;
++ }
++ return line_count;
++ }
++
++ void WriteFully(const void* buffer, size_t size) {
++ const char* ptr = static_cast(buffer);
++ while (size > 0) {
++ const size_t written = fwrite(ptr, 1, size, out_file_);
++ if (written == 0) {
++ UNREACHABLE();
++ break;
++ }
++ size -= written;
++ ptr += written;
++ }
++ }
++
++ const intptr_t pid_;
++
++ FILE* out_file_ = nullptr;
++ void* mapped_ = nullptr;
++ long mapped_size_ = 0; // NOLINT(runtime/int)
++
++ intptr_t code_id_ = 0;
++
++ DISALLOW_COPY_AND_ASSIGN(JitDumpCodeObserver);
++};
++
++#endif // !PRODUCT
++
++intptr_t OS::ProcessId() {
++ return static_cast(getpid());
++}
++
++static bool LocalTime(int64_t seconds_since_epoch, tm* tm_result) {
++ time_t seconds = static_cast(seconds_since_epoch);
++ if (seconds != seconds_since_epoch) return false;
++ struct tm* error_code = localtime_r(&seconds, tm_result);
++ return error_code != NULL;
++}
++
++const char* OS::GetTimeZoneName(int64_t seconds_since_epoch) {
++ tm decomposed;
++ bool succeeded = LocalTime(seconds_since_epoch, &decomposed);
++ // If unsuccessful, return an empty string like V8 does.
++ return (succeeded && (decomposed.tm_zone != NULL)) ? decomposed.tm_zone : "";
++}
++
++int OS::GetTimeZoneOffsetInSeconds(int64_t seconds_since_epoch) {
++ tm decomposed;
++ bool succeeded = LocalTime(seconds_since_epoch, &decomposed);
++ // Even if the offset was 24 hours it would still easily fit into 32 bits.
++ // If unsuccessful, return zero like V8 does.
++ return succeeded ? static_cast(decomposed.tm_gmtoff) : 0;
++}
++
++int64_t OS::GetCurrentTimeMillis() {
++ return GetCurrentTimeMicros() / 1000;
++}
++
++int64_t OS::GetCurrentTimeMicros() {
++ // gettimeofday has microsecond resolution.
++ struct timeval tv;
++ if (gettimeofday(&tv, NULL) < 0) {
++ UNREACHABLE();
++ return 0;
++ }
++ return (static_cast(tv.tv_sec) * 1000000) + tv.tv_usec;
++}
++
++int64_t OS::GetCurrentMonotonicTicks() {
++ struct timespec ts;
++ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
++ UNREACHABLE();
++ return 0;
++ }
++ // Convert to nanoseconds.
++ int64_t result = ts.tv_sec;
++ result *= kNanosecondsPerSecond;
++ result += ts.tv_nsec;
++ return result;
++}
++
++int64_t OS::GetCurrentMonotonicFrequency() {
++ return kNanosecondsPerSecond;
++}
++
++int64_t OS::GetCurrentMonotonicMicros() {
++ int64_t ticks = GetCurrentMonotonicTicks();
++ ASSERT(GetCurrentMonotonicFrequency() == kNanosecondsPerSecond);
++ return ticks / kNanosecondsPerMicrosecond;
++}
++
++int64_t OS::GetCurrentThreadCPUMicros() {
++ struct timespec ts;
++ if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) != 0) {
++ UNREACHABLE();
++ return -1;
++ }
++ int64_t result = ts.tv_sec;
++ result *= kMicrosecondsPerSecond;
++ result += (ts.tv_nsec / kNanosecondsPerMicrosecond);
++ return result;
++}
++
++int64_t OS::GetCurrentMonotonicMicrosForTimeline() {
++#if defined(SUPPORT_TIMELINE)
++ if (Timeline::recorder_discards_clock_values()) return -1;
++ return GetCurrentMonotonicMicros();
++#else
++ return -1;
++#endif
++}
++
++// TODO(5411554): May need to hoist these architecture dependent code
++// into a architecture specific file e.g: os_ia32_linux.cc
++intptr_t OS::ActivationFrameAlignment() {
++#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64) || \
++ defined(TARGET_ARCH_ARM64) || defined(TARGET_ARCH_RISCV32) || \
++ defined(TARGET_ARCH_RISCV64)
++ const int kMinimumAlignment = 16;
++#elif defined(TARGET_ARCH_ARM)
++ const int kMinimumAlignment = 8;
++#else
++#error Unsupported architecture.
++#endif
++ intptr_t alignment = kMinimumAlignment;
++ // TODO(5411554): Allow overriding default stack alignment for
++ // testing purposes.
++ // Flags::DebugIsInt("stackalign", &alignment);
++ ASSERT(Utils::IsPowerOfTwo(alignment));
++ ASSERT(alignment >= kMinimumAlignment);
++ return alignment;
++}
++
++int OS::NumberOfAvailableProcessors() {
++ return sysconf(_SC_NPROCESSORS_ONLN);
++}
++
++void OS::Sleep(int64_t millis) {
++ int64_t micros = millis * kMicrosecondsPerMillisecond;
++ SleepMicros(micros);
++}
++
++void OS::SleepMicros(int64_t micros) {
++ struct timespec req; // requested.
++ struct timespec rem; // remainder.
++ int64_t seconds = micros / kMicrosecondsPerSecond;
++ micros = micros - seconds * kMicrosecondsPerSecond;
++ int64_t nanos = micros * kNanosecondsPerMicrosecond;
++ req.tv_sec = seconds;
++ req.tv_nsec = nanos;
++ while (true) {
++ int r = nanosleep(&req, &rem);
++ if (r == 0) {
++ break;
++ }
++ // We should only ever see an interrupt error.
++ ASSERT(errno == EINTR);
++ // Copy remainder into requested and repeat.
++ req = rem;
++ }
++}
++
++// TODO(regis): Function called only from the simulator.
++void OS::DebugBreak() {
++ __builtin_trap();
++}
++
++DART_NOINLINE uintptr_t OS::GetProgramCounter() {
++ return reinterpret_cast(
++ __builtin_extract_return_addr(__builtin_return_address(0)));
++}
++
++void OS::Print(const char* format, ...) {
++ va_list args;
++ va_start(args, format);
++ ((void)OH_LOG_Print(LOG_APP, LOG_INFO, 0x0000, "DartVM", format, args));
++ va_end(args);
++}
++
++void OS::VFPrint(FILE* stream, const char* format, va_list args) {
++ vfprintf(stream, format, args);
++ fflush(stream);
++}
++
++char* OS::SCreate(Zone* zone, const char* format, ...) {
++ va_list args;
++ va_start(args, format);
++ char* buffer = VSCreate(zone, format, args);
++ va_end(args);
++ return buffer;
++}
++
++char* OS::VSCreate(Zone* zone, const char* format, va_list args) {
++ // Measure.
++ va_list measure_args;
++ va_copy(measure_args, args);
++ intptr_t len = Utils::VSNPrint(NULL, 0, format, measure_args);
++ va_end(measure_args);
++
++ char* buffer;
++ if (zone != nullptr) {
++ buffer = zone->Alloc(len + 1);
++ } else {
++ buffer = reinterpret_cast(malloc(len + 1));
++ }
++ ASSERT(buffer != NULL);
++
++ // Print.
++ va_list print_args;
++ va_copy(print_args, args);
++ Utils::VSNPrint(buffer, len + 1, format, print_args);
++ va_end(print_args);
++ return buffer;
++}
++
++bool OS::StringToInt64(const char* str, int64_t* value) {
++ ASSERT(str != NULL && strlen(str) > 0 && value != NULL);
++ int32_t base = 10;
++ char* endptr;
++ int i = 0;
++ if (str[0] == '-') {
++ i = 1;
++ } else if (str[0] == '+') {
++ i = 1;
++ }
++ if ((str[i] == '0') && (str[i + 1] == 'x' || str[i + 1] == 'X') &&
++ (str[i + 2] != '\0')) {
++ base = 16;
++ }
++ errno = 0;
++ if (base == 16) {
++ // Unsigned 64-bit hexadecimal integer literals are allowed but
++ // immediately interpreted as signed 64-bit integers.
++ *value = static_cast(strtoull(str, &endptr, base));
++ } else {
++ *value = strtoll(str, &endptr, base);
++ }
++ return ((errno == 0) && (endptr != str) && (*endptr == 0));
++}
++
++void OS::RegisterCodeObservers() {
++#ifndef PRODUCT
++ if (FLAG_generate_perf_events_symbols) {
++ CodeObservers::Register(new PerfCodeObserver);
++ }
++
++ if (FLAG_generate_perf_jitdump) {
++ CodeObservers::Register(new JitDumpCodeObserver);
++ }
++#endif // !PRODUCT
++}
++
++void OS::PrintErr(const char* format, ...) {
++ va_list args;
++ va_start(args, format);
++ ((void)OH_LOG_Print(LOG_APP, LOG_ERROR, 0x0000, "DartVM", format, args));
++ va_end(args);
++}
++
++void OS::Init() {}
++
++void OS::Cleanup() {}
++
++void OS::PrepareToAbort() {}
++
++void OS::Abort() {
++ PrepareToAbort();
++ abort();
++}
++
++void OS::Exit(int code) {
++ exit(code);
++}
++
++OS::BuildId OS::GetAppBuildId(const uint8_t* snapshot_instructions) {
++ // First return the build ID information from the instructions image if
++ // available.
++ const Image instructions_image(snapshot_instructions);
++ if (auto* const image_build_id = instructions_image.build_id()) {
++ return {instructions_image.build_id_length(), image_build_id};
++ }
++ Dl_info snapshot_info;
++ if (dladdr(snapshot_instructions, &snapshot_info) == 0) {
++ return {0, nullptr};
++ }
++ const uint8_t* dso_base =
++ static_cast(snapshot_info.dli_fbase);
++ const ElfW(Ehdr)& elf_header = *reinterpret_cast(dso_base);
++ const ElfW(Phdr)* const phdr_array =
++ reinterpret_cast(dso_base + elf_header.e_phoff);
++ for (intptr_t i = 0; i < elf_header.e_phnum; i++) {
++ const ElfW(Phdr)& header = phdr_array[i];
++ if (header.p_type != PT_NOTE) continue;
++ if ((header.p_flags & PF_R) != PF_R) continue;
++ const uint8_t* const note_addr = dso_base + header.p_vaddr;
++ const Elf32_Nhdr& note_header =
++ *reinterpret_cast(note_addr);
++ if (note_header.n_type != NT_GNU_BUILD_ID) continue;
++ const char* const note_contents =
++ reinterpret_cast(note_addr + sizeof(Elf32_Nhdr));
++ // The note name contains the null terminator as well.
++ if (note_header.n_namesz != strlen(ELF_NOTE_GNU) + 1) continue;
++ if (strncmp(ELF_NOTE_GNU, note_contents, note_header.n_namesz) == 0) {
++ return {static_cast(note_header.n_descsz),
++ reinterpret_cast(note_contents +
++ note_header.n_namesz)};
++ }
++ }
++ return {0, nullptr};
++}
++
++} // namespace dart
++
++#endif // defined(DART_HOST_OS_OHOS)
+diff --git a/runtime/vm/os_thread.h b/runtime/vm/os_thread.h
+index 0c768f6b23d..adb0aa62400 100644
+--- a/runtime/vm/os_thread.h
++++ b/runtime/vm/os_thread.h
+@@ -21,6 +21,8 @@
+ #include "vm/os_thread_fuchsia.h"
+ #elif defined(DART_HOST_OS_LINUX)
+ #include "vm/os_thread_linux.h"
++#elif defined(DART_HOST_OS_OHOS)
++#include "vm/os_thread_ohos.h"
+ #elif defined(DART_HOST_OS_MACOS)
+ #include "vm/os_thread_macos.h"
+ #elif defined(DART_HOST_OS_WINDOWS)
+diff --git a/runtime/vm/os_thread_absl.cc b/runtime/vm/os_thread_absl.cc
+index 8c4e7e2aecd..73cc2395238 100644
+--- a/runtime/vm/os_thread_absl.cc
++++ b/runtime/vm/os_thread_absl.cc
+@@ -110,7 +110,7 @@ static void UnblockSIGPROF() {
+ // is used to ensure that the thread is properly destroyed if the thread just
+ // exits.
+ static void* ThreadStart(void* data_ptr) {
+-#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX)
++#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS)
+ if (FLAG_worker_thread_priority != kMinInt) {
+ if (setpriority(PRIO_PROCESS, syscall(__NR_gettid),
+ FLAG_worker_thread_priority) == -1) {
+@@ -145,7 +145,7 @@ static void* ThreadStart(void* data_ptr) {
+ // pthread_setname_np ignores names that are too long rather than truncating.
+ char truncated_name[16];
+ snprintf(truncated_name, ARRAY_SIZE(truncated_name), "%s", name);
+-#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX)
++#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS)
+ pthread_setname_np(pthread_self(), truncated_name);
+ #elif defined(DART_HOST_OS_MACOS)
+ // Set the thread name.
+@@ -224,7 +224,7 @@ ThreadId OSThread::GetCurrentThreadId() {
+ ThreadId OSThread::GetCurrentThreadTraceId() {
+ #if defined(DART_HOST_OS_ANDROID)
+ return GetCurrentThreadId();
+-#elif defined(DART_HOST_OS_LINUX)
++#elif defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS)
+ return syscall(__NR_gettid);
+ #elif defined(DART_HOST_OS_MACOS)
+ return ThreadIdFromIntPtr(pthread_mach_thread_np(pthread_self()));
+@@ -265,7 +265,7 @@ void OSThread::Join(ThreadJoinId id) {
+
+ intptr_t OSThread::ThreadIdToIntPtr(ThreadId id) {
+ COMPILE_ASSERT(sizeof(id) <= sizeof(intptr_t));
+-#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX)
++#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS)
+ return static_cast(id);
+ #elif defined(DART_HOST_OS_MACOS)
+ return reinterpret_cast(id);
+@@ -273,7 +273,7 @@ intptr_t OSThread::ThreadIdToIntPtr(ThreadId id) {
+ }
+
+ ThreadId OSThread::ThreadIdFromIntPtr(intptr_t id) {
+-#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX)
++#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS)
+ return static_cast(id);
+ #elif defined(DART_HOST_OS_MACOS)
+ return reinterpret_cast(id);
+@@ -285,7 +285,7 @@ bool OSThread::Compare(ThreadId a, ThreadId b) {
+ }
+
+ bool OSThread::GetCurrentStackBounds(uword* lower, uword* upper) {
+-#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX)
++#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS)
+ pthread_attr_t attr;
+ // May fail on the main thread.
+ if (pthread_getattr_np(pthread_self(), &attr) != 0) {
+diff --git a/runtime/vm/os_thread_ohos.cc b/runtime/vm/os_thread_ohos.cc
+new file mode 100644
+index 00000000000..d5fbff8f79e
+--- /dev/null
++++ b/runtime/vm/os_thread_ohos.cc
+@@ -0,0 +1,508 @@
++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
++// for details. All rights reserved. Use of this source code is governed by a
++// BSD-style license that can be found in the LICENSE file.
++
++#include "platform/globals.h" // NOLINT
++
++#if defined(DART_HOST_OS_OHOS) && !defined(DART_USE_ABSL)
++
++#include "vm/os_thread.h"
++
++#include // NOLINT
++#include
++#include
++#include // NOLINT
++#include // NOLINT
++#include // NOLINT
++
++#include "platform/address_sanitizer.h"
++#include "platform/assert.h"
++#include "platform/safe_stack.h"
++#include "platform/signal_blocker.h"
++#include "platform/utils.h"
++
++#include "vm/flags.h"
++
++namespace dart {
++
++DEFINE_FLAG(int,
++ worker_thread_priority,
++ kMinInt,
++ "The thread priority the VM should use for new worker threads.");
++
++#define VALIDATE_PTHREAD_RESULT(result) \
++ if (result != 0) { \
++ const int kBufferSize = 1024; \
++ char error_buf[kBufferSize]; \
++ FATAL("pthread error: %d (%s)", result, \
++ Utils::StrError(result, error_buf, kBufferSize)); \
++ }
++
++// Variation of VALIDATE_PTHREAD_RESULT for named objects.
++#if defined(PRODUCT)
++#define VALIDATE_PTHREAD_RESULT_NAMED(result) VALIDATE_PTHREAD_RESULT(result)
++#else
++#define VALIDATE_PTHREAD_RESULT_NAMED(result) \
++ if (result != 0) { \
++ const int kBufferSize = 1024; \
++ char error_buf[kBufferSize]; \
++ FATAL("[%s] pthread error: %d (%s)", name_, result, \
++ Utils::StrError(result, error_buf, kBufferSize)); \
++ }
++#endif
++
++#if defined(DEBUG)
++#define ASSERT_PTHREAD_SUCCESS(result) VALIDATE_PTHREAD_RESULT(result)
++#else
++// NOTE: This (currently) expands to a no-op.
++#define ASSERT_PTHREAD_SUCCESS(result) ASSERT(result == 0)
++#endif
++
++#ifdef DEBUG
++#define RETURN_ON_PTHREAD_FAILURE(result) \
++ if (result != 0) { \
++ const int kBufferSize = 1024; \
++ char error_buf[kBufferSize]; \
++ fprintf(stderr, "%s:%d: pthread error: %d (%s)\n", __FILE__, __LINE__, \
++ result, Utils::StrError(result, error_buf, kBufferSize)); \
++ return result; \
++ }
++#else
++#define RETURN_ON_PTHREAD_FAILURE(result) \
++ if (result != 0) return result;
++#endif
++
++static void ComputeTimeSpecMicros(struct timespec* ts, int64_t micros) {
++ int64_t secs = micros / kMicrosecondsPerSecond;
++ int64_t nanos =
++ (micros - (secs * kMicrosecondsPerSecond)) * kNanosecondsPerMicrosecond;
++ int result = clock_gettime(CLOCK_MONOTONIC, ts);
++ ASSERT(result == 0);
++ ts->tv_sec += secs;
++ ts->tv_nsec += nanos;
++ if (ts->tv_nsec >= kNanosecondsPerSecond) {
++ ts->tv_sec += 1;
++ ts->tv_nsec -= kNanosecondsPerSecond;
++ }
++}
++
++class ThreadStartData {
++ public:
++ ThreadStartData(const char* name,
++ OSThread::ThreadStartFunction function,
++ uword parameter)
++ : name_(name), function_(function), parameter_(parameter) {}
++
++ const char* name() const { return name_; }
++ OSThread::ThreadStartFunction function() const { return function_; }
++ uword parameter() const { return parameter_; }
++
++ private:
++ const char* name_;
++ OSThread::ThreadStartFunction function_;
++ uword parameter_;
++
++ DISALLOW_COPY_AND_ASSIGN(ThreadStartData);
++};
++
++// TODO(bkonyi): remove this call once the prebuilt SDK is updated.
++// Spawned threads inherit their spawner's signal mask. We sometimes spawn
++// threads for running Dart code from a thread that is blocking SIGPROF.
++// This function explicitly unblocks SIGPROF so the profiler continues to
++// sample this thread.
++static void UnblockSIGPROF() {
++ sigset_t set;
++ sigemptyset(&set);
++ sigaddset(&set, SIGPROF);
++ int r = pthread_sigmask(SIG_UNBLOCK, &set, NULL);
++ USE(r);
++ ASSERT(r == 0);
++ ASSERT(!CHECK_IS_BLOCKING(SIGPROF));
++}
++
++// Dispatch to the thread start function provided by the caller. This trampoline
++// is used to ensure that the thread is properly destroyed if the thread just
++// exits.
++static void* ThreadStart(void* data_ptr) {
++ if (FLAG_worker_thread_priority != kMinInt) {
++ if (setpriority(PRIO_PROCESS, syscall(__NR_gettid),
++ FLAG_worker_thread_priority) == -1) {
++ FATAL("Setting thread priority to %d failed: errno = %d\n",
++ FLAG_worker_thread_priority, errno);
++ }
++ }
++
++ ThreadStartData* data = reinterpret_cast(data_ptr);
++
++ const char* name = data->name();
++ OSThread::ThreadStartFunction function = data->function();
++ uword parameter = data->parameter();
++ delete data;
++
++ // Set the thread name. There is 16 bytes limit on the name (including \0).
++ // pthread_setname_np ignores names that are too long rather than truncating.
++ char truncated_name[16];
++ snprintf(truncated_name, ARRAY_SIZE(truncated_name), "%s", name);
++ pthread_setname_np(pthread_self(), truncated_name);
++
++ // Create new OSThread object and set as TLS for new thread.
++ OSThread* thread = OSThread::CreateOSThread();
++ if (thread != NULL) {
++ OSThread::SetCurrent(thread);
++ thread->SetName(name);
++ UnblockSIGPROF();
++ // Call the supplied thread start function handing it its parameters.
++ function(parameter);
++ }
++
++ return NULL;
++}
++
++int OSThread::Start(const char* name,
++ ThreadStartFunction function,
++ uword parameter) {
++ pthread_attr_t attr;
++ int result = pthread_attr_init(&attr);
++ RETURN_ON_PTHREAD_FAILURE(result);
++
++ result = pthread_attr_setstacksize(&attr, OSThread::GetMaxStackSize());
++ RETURN_ON_PTHREAD_FAILURE(result);
++
++ ThreadStartData* data = new ThreadStartData(name, function, parameter);
++
++ pthread_t tid;
++ result = pthread_create(&tid, &attr, ThreadStart, data);
++ RETURN_ON_PTHREAD_FAILURE(result);
++
++ result = pthread_attr_destroy(&attr);
++ RETURN_ON_PTHREAD_FAILURE(result);
++
++ return 0;
++}
++
++const ThreadId OSThread::kInvalidThreadId = static_cast(0);
++const ThreadJoinId OSThread::kInvalidThreadJoinId =
++ static_cast(0);
++
++ThreadLocalKey OSThread::CreateThreadLocal(ThreadDestructor destructor) {
++ pthread_key_t key = kUnsetThreadLocalKey;
++ int result = pthread_key_create(&key, destructor);
++ VALIDATE_PTHREAD_RESULT(result);
++ ASSERT(key != kUnsetThreadLocalKey);
++ return key;
++}
++
++void OSThread::DeleteThreadLocal(ThreadLocalKey key) {
++ ASSERT(key != kUnsetThreadLocalKey);
++ int result = pthread_key_delete(key);
++ VALIDATE_PTHREAD_RESULT(result);
++}
++
++void OSThread::SetThreadLocal(ThreadLocalKey key, uword value) {
++ ASSERT(key != kUnsetThreadLocalKey);
++ int result = pthread_setspecific(key, reinterpret_cast(value));
++ VALIDATE_PTHREAD_RESULT(result);
++}
++
++intptr_t OSThread::GetMaxStackSize() {
++ const int kStackSize = (128 * kWordSize * KB);
++ return kStackSize;
++}
++
++ThreadId OSThread::GetCurrentThreadId() {
++ return pthread_self();
++}
++
++#ifdef SUPPORT_TIMELINE
++ThreadId OSThread::GetCurrentThreadTraceId() {
++ return syscall(__NR_gettid);
++}
++#endif // SUPPORT_TIMELINE
++
++char* OSThread::GetCurrentThreadName() {
++ const intptr_t kNameBufferSize = 16;
++ char* name = static_cast(malloc(kNameBufferSize));
++ prctl(PR_GET_NAME, name);
++ return name;
++}
++
++ThreadJoinId OSThread::GetCurrentThreadJoinId(OSThread* thread) {
++ ASSERT(thread != NULL);
++ // Make sure we're filling in the join id for the current thread.
++ ASSERT(thread->id() == GetCurrentThreadId());
++ // Make sure the join_id_ hasn't been set, yet.
++ DEBUG_ASSERT(thread->join_id_ == kInvalidThreadJoinId);
++ pthread_t id = pthread_self();
++#if defined(DEBUG)
++ thread->join_id_ = id;
++#endif
++ return id;
++}
++
++void OSThread::Join(ThreadJoinId id) {
++ int result = pthread_join(id, NULL);
++ ASSERT(result == 0);
++}
++
++intptr_t OSThread::ThreadIdToIntPtr(ThreadId id) {
++ ASSERT(sizeof(id) == sizeof(intptr_t));
++ return static_cast(id);
++}
++
++ThreadId OSThread::ThreadIdFromIntPtr(intptr_t id) {
++ return static_cast(id);
++}
++
++bool OSThread::Compare(ThreadId a, ThreadId b) {
++ return pthread_equal(a, b) != 0;
++}
++
++bool OSThread::GetCurrentStackBounds(uword* lower, uword* upper) {
++ pthread_attr_t attr;
++ // May fail on the main thread.
++ if (pthread_getattr_np(pthread_self(), &attr) != 0) {
++ return false;
++ }
++
++ void* base;
++ size_t size;
++ int error = pthread_attr_getstack(&attr, &base, &size);
++ pthread_attr_destroy(&attr);
++ if (error != 0) {
++ return false;
++ }
++
++ *lower = reinterpret_cast(base);
++ *upper = *lower + size;
++ return true;
++}
++
++#if defined(USING_SAFE_STACK)
++NO_SANITIZE_ADDRESS
++NO_SANITIZE_SAFE_STACK
++uword OSThread::GetCurrentSafestackPointer() {
++#error "SAFE_STACK is unsupported on this platform"
++ return 0;
++}
++
++NO_SANITIZE_ADDRESS
++NO_SANITIZE_SAFE_STACK
++void OSThread::SetCurrentSafestackPointer(uword ssp) {
++#error "SAFE_STACK is unsupported on this platform"
++}
++#endif
++
++Mutex::Mutex(NOT_IN_PRODUCT(const char* name))
++#if !defined(PRODUCT)
++ : name_(name)
++#endif
++{
++ pthread_mutexattr_t attr;
++ int result = pthread_mutexattr_init(&attr);
++ VALIDATE_PTHREAD_RESULT_NAMED(result);
++
++#if defined(DEBUG)
++ result = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
++ VALIDATE_PTHREAD_RESULT_NAMED(result);
++#endif // defined(DEBUG)
++
++ result = pthread_mutex_init(data_.mutex(), &attr);
++ // Verify that creating a pthread_mutex succeeded.
++ VALIDATE_PTHREAD_RESULT_NAMED(result);
++
++ result = pthread_mutexattr_destroy(&attr);
++ VALIDATE_PTHREAD_RESULT_NAMED(result);
++
++#if defined(DEBUG)
++ // When running with assertions enabled we track the owner.
++ owner_ = OSThread::kInvalidThreadId;
++#endif // defined(DEBUG)
++}
++
++Mutex::~Mutex() {
++ int result = pthread_mutex_destroy(data_.mutex());
++ // Verify that the pthread_mutex was destroyed.
++ VALIDATE_PTHREAD_RESULT_NAMED(result);
++
++#if defined(DEBUG)
++ // When running with assertions enabled we track the owner.
++ ASSERT(owner_ == OSThread::kInvalidThreadId);
++#endif // defined(DEBUG)
++}
++
++void Mutex::Lock() {
++ int result = pthread_mutex_lock(data_.mutex());
++ // Specifically check for dead lock to help debugging.
++ ASSERT(result != EDEADLK);
++ ASSERT_PTHREAD_SUCCESS(result); // Verify no other errors.
++#if defined(DEBUG)
++ // When running with assertions enabled we track the owner.
++ owner_ = OSThread::GetCurrentThreadId();
++#endif // defined(DEBUG)
++}
++
++bool Mutex::TryLock() {
++ int result = pthread_mutex_trylock(data_.mutex());
++ // Return false if the lock is busy and locking failed.
++ if (result == EBUSY) {
++ return false;
++ }
++ ASSERT_PTHREAD_SUCCESS(result); // Verify no other errors.
++#if defined(DEBUG)
++ // When running with assertions enabled we track the owner.
++ owner_ = OSThread::GetCurrentThreadId();
++#endif // defined(DEBUG)
++ return true;
++}
++
++void Mutex::Unlock() {
++#if defined(DEBUG)
++ // When running with assertions enabled we track the owner.
++ ASSERT(IsOwnedByCurrentThread());
++ owner_ = OSThread::kInvalidThreadId;
++#endif // defined(DEBUG)
++ int result = pthread_mutex_unlock(data_.mutex());
++ // Specifically check for wrong thread unlocking to aid debugging.
++ ASSERT(result != EPERM);
++ ASSERT_PTHREAD_SUCCESS(result); // Verify no other errors.
++}
++
++Monitor::Monitor() {
++ pthread_mutexattr_t mutex_attr;
++ int result = pthread_mutexattr_init(&mutex_attr);
++ VALIDATE_PTHREAD_RESULT(result);
++
++#if defined(DEBUG)
++ result = pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_ERRORCHECK);
++ VALIDATE_PTHREAD_RESULT(result);
++#endif // defined(DEBUG)
++
++ result = pthread_mutex_init(data_.mutex(), &mutex_attr);
++ VALIDATE_PTHREAD_RESULT(result);
++
++ result = pthread_mutexattr_destroy(&mutex_attr);
++ VALIDATE_PTHREAD_RESULT(result);
++
++ pthread_condattr_t cond_attr;
++ result = pthread_condattr_init(&cond_attr);
++ VALIDATE_PTHREAD_RESULT(result);
++
++ result = pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC);
++ VALIDATE_PTHREAD_RESULT(result);
++
++ result = pthread_cond_init(data_.cond(), &cond_attr);
++ VALIDATE_PTHREAD_RESULT(result);
++
++ result = pthread_condattr_destroy(&cond_attr);
++ VALIDATE_PTHREAD_RESULT(result);
++
++#if defined(DEBUG)
++ // When running with assertions enabled we track the owner.
++ owner_ = OSThread::kInvalidThreadId;
++#endif // defined(DEBUG)
++}
++
++Monitor::~Monitor() {
++#if defined(DEBUG)
++ // When running with assertions enabled we track the owner.
++ ASSERT(owner_ == OSThread::kInvalidThreadId);
++#endif // defined(DEBUG)
++
++ int result = pthread_mutex_destroy(data_.mutex());
++ VALIDATE_PTHREAD_RESULT(result);
++
++ result = pthread_cond_destroy(data_.cond());
++ VALIDATE_PTHREAD_RESULT(result);
++}
++
++bool Monitor::TryEnter() {
++ int result = pthread_mutex_trylock(data_.mutex());
++ // Return false if the lock is busy and locking failed.
++ if (result == EBUSY) {
++ return false;
++ }
++ ASSERT_PTHREAD_SUCCESS(result); // Verify no other errors.
++#if defined(DEBUG)
++ // When running with assertions enabled we track the owner.
++ ASSERT(owner_ == OSThread::kInvalidThreadId);
++ owner_ = OSThread::GetCurrentThreadId();
++#endif // defined(DEBUG)
++ return true;
++}
++
++void Monitor::Enter() {
++ int result = pthread_mutex_lock(data_.mutex());
++ VALIDATE_PTHREAD_RESULT(result);
++
++#if defined(DEBUG)
++ // When running with assertions enabled we track the owner.
++ ASSERT(owner_ == OSThread::kInvalidThreadId);
++ owner_ = OSThread::GetCurrentThreadId();
++#endif // defined(DEBUG)
++}
++
++void Monitor::Exit() {
++#if defined(DEBUG)
++ // When running with assertions enabled we track the owner.
++ ASSERT(IsOwnedByCurrentThread());
++ owner_ = OSThread::kInvalidThreadId;
++#endif // defined(DEBUG)
++
++ int result = pthread_mutex_unlock(data_.mutex());
++ VALIDATE_PTHREAD_RESULT(result);
++}
++
++Monitor::WaitResult Monitor::Wait(int64_t millis) {
++ Monitor::WaitResult retval = WaitMicros(millis * kMicrosecondsPerMillisecond);
++ return retval;
++}
++
++Monitor::WaitResult Monitor::WaitMicros(int64_t micros) {
++#if defined(DEBUG)
++ // When running with assertions enabled we track the owner.
++ ASSERT(IsOwnedByCurrentThread());
++ ThreadId saved_owner = owner_;
++ owner_ = OSThread::kInvalidThreadId;
++#endif // defined(DEBUG)
++
++ Monitor::WaitResult retval = kNotified;
++ if (micros == kNoTimeout) {
++ // Wait forever.
++ int result = pthread_cond_wait(data_.cond(), data_.mutex());
++ VALIDATE_PTHREAD_RESULT(result);
++ } else {
++ struct timespec ts;
++ ComputeTimeSpecMicros(&ts, micros);
++ int result = pthread_cond_timedwait(data_.cond(), data_.mutex(), &ts);
++ ASSERT((result == 0) || (result == ETIMEDOUT));
++ if (result == ETIMEDOUT) {
++ retval = kTimedOut;
++ }
++ }
++
++#if defined(DEBUG)
++ // When running with assertions enabled we track the owner.
++ ASSERT(owner_ == OSThread::kInvalidThreadId);
++ owner_ = OSThread::GetCurrentThreadId();
++ ASSERT(owner_ == saved_owner);
++#endif // defined(DEBUG)
++ return retval;
++}
++
++void Monitor::Notify() {
++ // When running with assertions enabled we track the owner.
++ ASSERT(IsOwnedByCurrentThread());
++ int result = pthread_cond_signal(data_.cond());
++ VALIDATE_PTHREAD_RESULT(result);
++}
++
++void Monitor::NotifyAll() {
++ // When running with assertions enabled we track the owner.
++ ASSERT(IsOwnedByCurrentThread());
++ int result = pthread_cond_broadcast(data_.cond());
++ VALIDATE_PTHREAD_RESULT(result);
++}
++
++} // namespace dart
++
++#endif // defined(DART_HOST_OS_OHOS) && !defined(DART_USE_ABSL)
+diff --git a/runtime/vm/os_thread_ohos.h b/runtime/vm/os_thread_ohos.h
+new file mode 100644
+index 00000000000..aaf490f9f77
+--- /dev/null
++++ b/runtime/vm/os_thread_ohos.h
+@@ -0,0 +1,76 @@
++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
++// for details. All rights reserved. Use of this source code is governed by a
++// BSD-style license that can be found in the LICENSE file.
++
++#ifndef RUNTIME_VM_OS_THREAD_OHOS_H_
++#define RUNTIME_VM_OS_THREAD_OHOS_H_
++
++#if !defined(RUNTIME_VM_OS_THREAD_H_)
++#error Do not include os_thread_linux.h directly; use os_thread.h instead.
++#endif
++
++#include
++
++#include "platform/assert.h"
++#include "platform/globals.h"
++
++namespace dart {
++
++typedef pthread_key_t ThreadLocalKey;
++typedef pthread_t ThreadId;
++typedef pthread_t ThreadJoinId;
++
++static const ThreadLocalKey kUnsetThreadLocalKey =
++ static_cast(-1);
++
++class ThreadInlineImpl {
++ private:
++ ThreadInlineImpl() {}
++ ~ThreadInlineImpl() {}
++
++ static uword GetThreadLocal(ThreadLocalKey key) {
++ ASSERT(key != kUnsetThreadLocalKey);
++ return reinterpret_cast(pthread_getspecific(key));
++ }
++
++ friend class OSThread;
++
++ DISALLOW_ALLOCATION();
++ DISALLOW_COPY_AND_ASSIGN(ThreadInlineImpl);
++};
++
++class MutexData {
++ private:
++ MutexData() {}
++ ~MutexData() {}
++
++ pthread_mutex_t* mutex() { return &mutex_; }
++
++ pthread_mutex_t mutex_;
++
++ friend class Mutex;
++
++ DISALLOW_ALLOCATION();
++ DISALLOW_COPY_AND_ASSIGN(MutexData);
++};
++
++class MonitorData {
++ private:
++ MonitorData() {}
++ ~MonitorData() {}
++
++ pthread_mutex_t* mutex() { return &mutex_; }
++ pthread_cond_t* cond() { return &cond_; }
++
++ pthread_mutex_t mutex_;
++ pthread_cond_t cond_;
++
++ friend class Monitor;
++
++ DISALLOW_ALLOCATION();
++ DISALLOW_COPY_AND_ASSIGN(MonitorData);
++};
++
++} // namespace dart
++
++#endif // RUNTIME_VM_OS_THREAD_OHOS_H_
+diff --git a/runtime/vm/proccpuinfo.cc b/runtime/vm/proccpuinfo.cc
+index a6c1c250d1b..b955e6d7838 100644
+--- a/runtime/vm/proccpuinfo.cc
++++ b/runtime/vm/proccpuinfo.cc
+@@ -3,7 +3,7 @@
+ // BSD-style license that can be found in the LICENSE file.
+
+ #include "vm/globals.h"
+-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS)
+
+ #include "vm/proccpuinfo.h"
+
+@@ -146,4 +146,4 @@ bool ProcCpuInfo::HasField(const char* field) {
+
+ } // namespace dart
+
+-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS)
+diff --git a/runtime/vm/proccpuinfo.h b/runtime/vm/proccpuinfo.h
+index ff249d42d2d..4c829f4ace6 100644
+--- a/runtime/vm/proccpuinfo.h
++++ b/runtime/vm/proccpuinfo.h
+@@ -6,7 +6,7 @@
+ #define RUNTIME_VM_PROCCPUINFO_H_
+
+ #include "vm/globals.h"
+-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS)
+
+ #include "vm/allocation.h"
+
+@@ -29,6 +29,6 @@ class ProcCpuInfo : public AllStatic {
+
+ } // namespace dart
+
+-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS)
+
+ #endif // RUNTIME_VM_PROCCPUINFO_H_
+diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc
+index 6b64f4d4f5a..85a577b2c9e 100644
+--- a/runtime/vm/profiler.cc
++++ b/runtime/vm/profiler.cc
+@@ -435,7 +435,7 @@ void Profiler::DumpStackTrace(void* context) {
+ return;
+ }
+ #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
+- defined(DART_HOST_OS_ANDROID)
++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS)
+ ucontext_t* ucontext = reinterpret_cast(context);
+ mcontext_t mcontext = ucontext->uc_mcontext;
+ uword pc = SignalHandler::GetProgramCounter(mcontext);
+diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
+index 3c5593ea4f9..ebb9f661591 100644
+--- a/runtime/vm/service.cc
++++ b/runtime/vm/service.cc
+@@ -4602,7 +4602,7 @@ static void RequestHeapSnapshot(Thread* thread, JSONStream* js) {
+ PrintSuccess(js);
+ }
+
+-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS)
+ struct VMMapping {
+ char path[256];
+ size_t size;
+@@ -4810,7 +4810,7 @@ static intptr_t GetProcessMemoryUsageHelper(JSONStream* js) {
+ vm.AddProperty64("size", vm_size);
+ }
+
+-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS)
+ AddVMMappings(&rss_children);
+ #endif
+ // TODO(46166): Implement for other operating systems.
+diff --git a/runtime/vm/signal_handler.h b/runtime/vm/signal_handler.h
+index bfcd982e915..fab0a1cea81 100644
+--- a/runtime/vm/signal_handler.h
++++ b/runtime/vm/signal_handler.h
+@@ -33,6 +33,9 @@ typedef struct ucontext {
+ struct siginfo_t;
+ struct mcontext_t;
+ struct sigset_t {};
++#elif defined(DART_HOST_OS_OHOS)
++#include // NOLINT
++#include // NOLINT
+ #elif defined(DART_HOST_OS_FUCHSIA)
+ #include // NOLINT
+ #include // NOLINT
+diff --git a/runtime/vm/signal_handler_ohos.cc b/runtime/vm/signal_handler_ohos.cc
+new file mode 100644
+index 00000000000..c64c9984606
+--- /dev/null
++++ b/runtime/vm/signal_handler_ohos.cc
+@@ -0,0 +1,136 @@
++// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
++// for details. All rights reserved. Use of this source code is governed by a
++// BSD-style license that can be found in the LICENSE file.
++
++#include "vm/globals.h"
++#include "vm/instructions.h"
++#include "vm/signal_handler.h"
++#include "vm/simulator.h"
++#if defined(DART_HOST_OS_OHOS)
++
++namespace dart {
++
++uintptr_t SignalHandler::GetProgramCounter(const mcontext_t& mcontext) {
++ uintptr_t pc = 0;
++
++#if defined(HOST_ARCH_IA32)
++ pc = static_cast(mcontext.gregs[REG_EIP]);
++#elif defined(HOST_ARCH_X64)
++ pc = static_cast(mcontext.gregs[REG_RIP]);
++#elif defined(HOST_ARCH_ARM)
++ pc = static_cast(mcontext.arm_pc);
++#elif defined(HOST_ARCH_ARM64)
++ pc = static_cast(mcontext.pc);
++#elif defined(HOST_ARCH_RISCV32)
++ pc = static_cast(mcontext.__gregs[REG_PC]);
++#elif defined(HOST_ARCH_RISCV64)
++ pc = static_cast(mcontext.__gregs[REG_PC]);
++#else
++#error Unsupported architecture.
++#endif // HOST_ARCH_...
++ return pc;
++}
++
++uintptr_t SignalHandler::GetFramePointer(const mcontext_t& mcontext) {
++ uintptr_t fp = 0;
++
++#if defined(HOST_ARCH_IA32)
++ fp = static_cast(mcontext.gregs[REG_EBP]);
++#elif defined(HOST_ARCH_X64)
++ fp = static_cast(mcontext.gregs[REG_RBP]);
++#elif defined(HOST_ARCH_ARM)
++ // B1.3.3 Program Status Registers (PSRs)
++ if ((mcontext.arm_cpsr & (1 << 5)) != 0) {
++ // Thumb mode.
++ fp = static_cast(mcontext.arm_r7);
++ } else {
++ // ARM mode.
++ fp = static_cast