@ohos-cpf/3rdloop 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (393) hide show
  1. package/README.md +245 -425
  2. package/bin/3rdloop.mjs +46 -46
  3. package/lib/cli.js +781 -740
  4. package/lib/config-cmd.js +452 -0
  5. package/lib/config.js +318 -317
  6. package/lib/doctor.js +113 -113
  7. package/lib/exit-codes.js +30 -30
  8. package/lib/opencode.js +155 -155
  9. package/lib/orch.js +426 -426
  10. package/lib/queries.js +263 -263
  11. package/lib/step.js +325 -325
  12. package/lib/ui.js +222 -222
  13. package/lib/update.js +280 -0
  14. package/lib/workflow.js +308 -290
  15. package/lib/workflows.js +131 -131
  16. package/package.json +50 -50
  17. package/vendor/Server/Agent/SkillSelector/README.md +343 -343
  18. package/vendor/Server/Agent/SkillSelector/core/Agent.js +207 -207
  19. package/vendor/Server/Agent/SkillSelector/index.js +14 -14
  20. package/vendor/Server/Agent/SkillSelector/llm/llmClient.js +313 -313
  21. package/vendor/Server/Agent/SkillSelector/llm/prompts.js +161 -161
  22. package/vendor/Server/Agent/SkillSelector/llm/reactSteps.js +144 -144
  23. package/vendor/Server/Agent/SkillSelector/matcher/fuzzyMatcher.js +433 -433
  24. package/vendor/Server/Agent/SkillSelector/scanner/skillScanner.js +382 -382
  25. package/vendor/Server/Agent/SkillSelector/utils/actionWords.js +59 -59
  26. package/vendor/Server/Brain/AnalysisStrategies.js +422 -422
  27. package/vendor/Server/Brain/Brain.js +686 -686
  28. package/vendor/Server/Brain/DecompositionStrategies.js +621 -621
  29. package/vendor/Server/Brain/PlanPersistence.js +196 -196
  30. package/vendor/Server/Brain/SkillExecutor.js +251 -251
  31. package/vendor/Server/Brain/index.js +11 -11
  32. package/vendor/Server/CLI/cli.js +275 -243
  33. package/vendor/Server/CLI/deveco-code/index.js +71 -0
  34. package/vendor/Server/CLI/deveco-code.js +5 -0
  35. package/vendor/Server/CLI/opencode/index.js +817 -804
  36. package/vendor/Server/CLI/opencode.js +5 -5
  37. package/vendor/Server/DbUse/MessageArchive.js +61 -61
  38. package/vendor/Server/DbUse/StorageManager.js +107 -107
  39. package/vendor/Server/DbUse/adapters/BaseAdapter.js +94 -94
  40. package/vendor/Server/DbUse/adapters/FileAdapter.js +138 -138
  41. package/vendor/Server/DbUse/adapters/JsonAdapter.js +138 -138
  42. package/vendor/Server/DbUse/adapters/JsonlAdapter.js +114 -114
  43. package/vendor/Server/DbUse/adapters/SqliteAdapter.js +157 -157
  44. package/vendor/Server/DbUse/index.js +160 -160
  45. package/vendor/Server/DbUse/stores/SessionStore.js +279 -279
  46. package/vendor/Server/DbUse/stores/TaskExecutionStore.js +133 -133
  47. package/vendor/Server/DbUse/stores/TaskRegistryStore.js +262 -262
  48. package/vendor/Server/DbUse/stores/TaskStepStore.js +279 -279
  49. package/vendor/Server/FlexRunner/FlexRunner.js +1759 -1759
  50. package/vendor/Server/FlexRunner/index.js +12 -12
  51. package/vendor/Server/Knowledge/KnowledgeImporter.js +523 -523
  52. package/vendor/Server/Knowledge/KnowledgeManager.js +341 -341
  53. package/vendor/Server/Knowledge/index.js +33 -33
  54. package/vendor/Server/LoopEngine/LoopEngine.js +1133 -1133
  55. package/vendor/Server/LoopEngine/index.js +11 -11
  56. package/vendor/Server/Orchestrator/Orchestrator.js +0 -3
  57. package/vendor/Server/Orchestrator/StepNode.js +312 -312
  58. package/vendor/Server/Orchestrator/index.js +13 -13
  59. package/vendor/Server/Routes/controllers/BrainController.js +241 -241
  60. package/vendor/Server/Routes/controllers/FlexRunnerController.js +1125 -1125
  61. package/vendor/Server/Routes/controllers/HealthController.js +44 -44
  62. package/vendor/Server/Routes/controllers/KnowledgeImportController.js +355 -355
  63. package/vendor/Server/Routes/controllers/LoopEngineController.js +788 -788
  64. package/vendor/Server/Routes/controllers/StatsController.js +57 -57
  65. package/vendor/Server/Routes/controllers/StreamController.js +67 -67
  66. package/vendor/Server/Routes/library/logger.js +179 -179
  67. package/vendor/Server/Routes/library/zipArchive.js +172 -172
  68. package/vendor/Server/Routes/router/Router.js +204 -204
  69. package/vendor/Server/Routes/routes/brain.js +47 -47
  70. package/vendor/Server/Routes/routes/flexrunner.js +72 -72
  71. package/vendor/Server/Routes/routes/health.js +28 -28
  72. package/vendor/Server/Routes/routes/index.js +57 -57
  73. package/vendor/Server/Routes/routes/knowledge.js +55 -55
  74. package/vendor/Server/Routes/routes/loopengine.js +58 -58
  75. package/vendor/Server/Routes/routes/orchestrator.js +61 -61
  76. package/vendor/Server/Routes/routes/registry.js +97 -97
  77. package/vendor/Server/Routes/routes/stats.js +23 -23
  78. package/vendor/Server/Routes/routes/stream.js +30 -30
  79. package/vendor/Server/Routes/server.js +203 -202
  80. package/vendor/Server/Routes/sse/SseHub.js +308 -308
  81. package/vendor/Server/Skills/acceptance-check/SKILL.md +191 -174
  82. package/vendor/Server/Skills/acceptance-check/references/PHASE1_ARTIFACT_COLLECTION.md +52 -52
  83. package/vendor/Server/Skills/acceptance-check/references/PHASE2_CRITERIA_EVALUATION.md +77 -77
  84. package/vendor/Server/Skills/acceptance-check/references/PHASE2_EVIDENCE_VERIFICATION.md +70 -0
  85. package/vendor/Server/Skills/arkts-code-check/SKILL.md +320 -313
  86. package/vendor/Server/Skills/arkts-code-check/references/CODE_CHECK_GUIDE.md +210 -159
  87. package/vendor/Server/Skills/arkts-code-check/references/FILE_TYPE_GUIDE.md +28 -28
  88. package/vendor/Server/Skills/arkts-code-check/references/PHASE1_INFO_COLLECTION.md +233 -222
  89. package/vendor/Server/Skills/arkts-code-check/references/REPORT_TEMPLATE.md +219 -219
  90. package/vendor/Server/Skills/arkts-library-compile-validate/SKILL.md +328 -328
  91. package/vendor/Server/Skills/arkts-library-compile-validate/scripts/build.ps1 +93 -93
  92. package/vendor/Server/Skills/arkts-library-compile-validate/scripts/code-linter.cjs +432 -432
  93. package/vendor/Server/Skills/arkts-library-compile-validate/scripts/migrate.cjs +851 -851
  94. package/vendor/Server/Skills/arkts-library-compile-validate/scripts/ohpm-switch.cjs +788 -788
  95. package/vendor/Server/Skills/arkts-library-demo-code/SKILL.md +291 -291
  96. package/vendor/Server/Skills/arkts-library-demo-code/references/API_SIGNATURE_CHECKS.md +68 -68
  97. package/vendor/Server/Skills/arkts-library-demo-code/references/BUILD_VALIDATION.md +184 -184
  98. package/vendor/Server/Skills/arkts-library-demo-code/references/CODE_STANDARDS.md +337 -337
  99. package/vendor/Server/Skills/arkts-library-demo-code/references/EXAMPLES.md +400 -400
  100. package/vendor/Server/Skills/arkts-library-demo-code/references/SYSTEM_API_LOOKUP.md +98 -98
  101. package/vendor/Server/Skills/arkts-library-demo-code/references/TEMPLATE.md +544 -544
  102. package/vendor/Server/Skills/arkts-library-demo-code/scripts/build.ps1 +93 -93
  103. package/vendor/Server/Skills/arkts-library-demo-code/scripts/code-linter.cjs +432 -432
  104. package/vendor/Server/Skills/arkts-library-demo-code/scripts/migrate.cjs +851 -851
  105. package/vendor/Server/Skills/arkts-library-demo-code/scripts/ohpm-switch.cjs +788 -788
  106. package/vendor/Server/Skills/arkts-library-demo-coverage/SKILL.md +174 -174
  107. package/vendor/Server/Skills/arkts-library-demo-scenario/SKILL.md +321 -321
  108. package/vendor/Server/Skills/arkts-library-demo-scenario/references/EXAMPLES.md +100 -100
  109. package/vendor/Server/Skills/arkts-library-demo-scenario/references/TEMPLATE.md +185 -185
  110. package/vendor/Server/Skills/arkts-library-documentation-quality-check/SKILL.md +233 -233
  111. package/vendor/Server/Skills/arkts-library-documentation-quality-check/assets/REPORT_TEMPLATE.md +102 -102
  112. package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/CHECK_DIMENSIONS.md +89 -89
  113. package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/COMPLIANCE_CHECKLIST.md +62 -62
  114. package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/KB_QUERY_GUIDE.md +107 -107
  115. package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/MCP_TOOL_DISCOVERY.md +124 -124
  116. package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/REPORT_TEMPLATE.md +90 -90
  117. package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/SCORING_FORMULA.md +57 -57
  118. package/vendor/Server/Skills/arkts-library-documentation-quality-check/scripts/validate-skill.cjs +259 -259
  119. package/vendor/Server/Skills/arkts-library-interface-spec/SKILL.md +97 -97
  120. package/vendor/Server/Skills/arkts-library-memory-leak-check/SKILL.md +391 -391
  121. package/vendor/Server/Skills/arkts-library-source-clone/SKILL.md +113 -113
  122. package/vendor/Server/Skills/arkts-library-stability-check/SKILL.md +312 -312
  123. package/vendor/Server/Skills/arkts-library-stability-check/assets/REPORT_TEMPLATE.md +154 -154
  124. package/vendor/Server/Skills/arkts-library-stability-check/references/CHECKLIST.md +165 -165
  125. package/vendor/Server/Skills/arkts-library-stability-check/references/CODE_EXAMPLES.md +308 -308
  126. package/vendor/Server/Skills/arkts-library-stability-check/references/SCORING_RULES.md +90 -90
  127. package/vendor/Server/Skills/arkts-library-stability-check/scripts/validate-skill.cjs +282 -282
  128. package/vendor/Server/Skills/arkts-library-test-coverage-check/SKILL.md +360 -360
  129. package/vendor/Server/Skills/arkts-library-test-coverage-check/assets/REPORT_TEMPLATE.md +145 -145
  130. package/vendor/Server/Skills/arkts-library-test-coverage-check/references/CHECKLIST.md +252 -252
  131. package/vendor/Server/Skills/arkts-library-test-coverage-check/references/MCP_TOOL_USAGE.md +114 -114
  132. package/vendor/Server/Skills/arkts-library-test-coverage-check/scripts/validate-skill.cjs +262 -262
  133. package/vendor/Server/Skills/arkts-library-xts-code/SKILL.md +455 -455
  134. package/vendor/Server/Skills/arkts-library-xts-code/assets/report-template.md +60 -60
  135. package/vendor/Server/Skills/arkts-library-xts-code/references/ACCESS_MODIFIER_PRECHECK.md +76 -76
  136. package/vendor/Server/Skills/arkts-library-xts-code/references/BUILD_VERIFICATION.md +200 -200
  137. package/vendor/Server/Skills/arkts-library-xts-code/references/CODE_STANDARDS.md +518 -518
  138. package/vendor/Server/Skills/arkts-library-xts-code/references/SUBAGENT_PROMPT_TEMPLATE.md +276 -276
  139. package/vendor/Server/Skills/arkts-library-xts-code/references/SYSTEM_API_LOOKUP.md +92 -92
  140. package/vendor/Server/Skills/arkts-library-xts-code/references/XTS_TEMPLATE.md +767 -767
  141. package/vendor/Server/Skills/arkts-library-xts-code/scripts/build.ps1 +93 -93
  142. package/vendor/Server/Skills/arkts-library-xts-code/scripts/code-linter.cjs +432 -432
  143. package/vendor/Server/Skills/arkts-library-xts-code/scripts/migrate.cjs +851 -851
  144. package/vendor/Server/Skills/arkts-library-xts-code/scripts/ohpm-switch.cjs +788 -788
  145. package/vendor/Server/Skills/arkts-library-xts-code/scripts/precheck-access-modifiers.cjs +221 -221
  146. package/vendor/Server/Skills/arkts-library-xts-code-fix/SKILL.md +228 -228
  147. package/vendor/Server/Skills/arkts-library-xts-coverage/SKILL.md +97 -97
  148. package/vendor/Server/Skills/arkts-library-xts-doc/SKILL.md +194 -194
  149. package/vendor/Server/Skills/arkts-library-xts-doc/references/EXAMPLES.md +575 -575
  150. package/vendor/Server/Skills/arkts-library-xts-doc/references/TEMPLATE.md +574 -574
  151. package/vendor/Server/Skills/arkts-library-xts-doc/references/TESTABILITY_GUIDE.md +182 -182
  152. package/vendor/Server/Skills/arkts-library-xts-execution/SKILL.md +490 -490
  153. package/vendor/Server/Skills/arkts-library-xts-execution/references/error-fixing-examples.md +217 -217
  154. package/vendor/Server/Skills/arkts-library-xts-execution/scripts/build.ps1 +93 -93
  155. package/vendor/Server/Skills/arkts-library-xts-execution/scripts/code-linter.cjs +432 -432
  156. package/vendor/Server/Skills/arkts-library-xts-execution/scripts/migrate.cjs +851 -851
  157. package/vendor/Server/Skills/arkts-library-xts-execution/scripts/ohpm-switch.cjs +788 -788
  158. package/vendor/Server/Skills/flutter-code-check/SKILL.md +299 -299
  159. package/vendor/Server/Skills/flutter-code-check/assets/OUTPUT_TEMPLATE.md +311 -311
  160. package/vendor/Server/Skills/flutter-code-check/references/CHECKLIST.md +190 -190
  161. package/vendor/Server/Skills/flutter-code-check/references/PLATFORMS.md +133 -133
  162. package/vendor/Server/Skills/flutter-code-check/scripts/validate-skill.cjs +259 -259
  163. package/vendor/Server/Skills/flutter-interface/README.md +75 -75
  164. package/vendor/Server/Skills/flutter-interface/SKILL.md +291 -291
  165. package/vendor/Server/Skills/flutter-interface/references/EXAMPLES.md +336 -336
  166. package/vendor/Server/Skills/flutter-interface/references/GIT_CLONE_GUIDE.md +190 -190
  167. package/vendor/Server/Skills/flutter-interface/references/SCRIPT_REFERENCE.md +258 -258
  168. package/vendor/Server/Skills/flutter-interface/references/TEMPLATE.md +254 -254
  169. package/vendor/Server/Skills/flutter-library-consistency-check/SKILL.md +517 -517
  170. package/vendor/Server/Skills/flutter-library-consistency-check/references/CHECKLIST.md +381 -381
  171. package/vendor/Server/Skills/flutter-library-consistency-check/references/FUNCTION_COMPARISON_GUIDE.md +341 -341
  172. package/vendor/Server/Skills/flutter-library-consistency-check/references/PLATFORM_LIMITATION_GUIDE.md +71 -71
  173. package/vendor/Server/Skills/flutter-library-documentation-quality-check/SKILL.md +518 -518
  174. package/vendor/Server/Skills/flutter-library-documentation-quality-check/references/CHECKLIST.md +406 -406
  175. package/vendor/Server/Skills/flutter-library-memory-leak-check/SKILL.md +652 -652
  176. package/vendor/Server/Skills/flutter-library-memory-leak-check/references/CHECKLIST.md +396 -396
  177. package/vendor/Server/Skills/flutter-library-stability-check/SKILL.md +744 -744
  178. package/vendor/Server/Skills/flutter-library-stability-check/references/CHECKLIST.md +720 -720
  179. package/vendor/Server/Skills/flutter-library-test-coverage-check/SKILL.md +414 -414
  180. package/vendor/Server/Skills/flutter-library-test-coverage-check/assets/REPORT_TEMPLATE.md +150 -150
  181. package/vendor/Server/Skills/flutter-library-test-coverage-check/references/CHECKLIST.md +304 -304
  182. package/vendor/Server/Skills/flutter-library-test-coverage-check/references/SCORING_RULES.md +265 -265
  183. package/vendor/Server/Skills/flutter-library-test-coverage-check/references/SCRIPT_USAGE.md +286 -286
  184. package/vendor/Server/Skills/knowledge-import/SKILL.md +383 -383
  185. package/vendor/Server/Skills/knowledge-import/references/MCP_TOOL_USAGE.md +165 -165
  186. package/vendor/Server/Skills/knowledge-import/references/SKILL_LOCATOR_GUIDE.md +91 -91
  187. package/vendor/Server/Skills/knowledge-import/references/SKILL_OPTIMIZATION_GUIDE.md +127 -127
  188. package/vendor/Server/Skills/ohos-lib-pr-push/SKILL.md +453 -301
  189. package/vendor/Server/Skills/ohos-lib-pr-push/assets/PR_CHECKLIST.md +151 -0
  190. package/vendor/Server/Skills/ohos-lib-pr-push/assets/PR_ISSUE_TEMPLATE.md +257 -105
  191. package/vendor/Server/Skills/ohos-lib-pr-push/references/GITCODE_API.md +191 -127
  192. package/vendor/Server/Skills/ohos-lib-pr-push/scripts/gitcode-api.cjs +317 -208
  193. package/vendor/Server/Skills/rn-code-check/SKILL.md +320 -320
  194. package/vendor/Server/Skills/rn-code-check/references/API-GUIDE.md +234 -234
  195. package/vendor/Server/Skills/rn-code-check/references/CHECKLIST.md +188 -188
  196. package/vendor/Server/Skills/rn-code-check/references/REPORT-TEMPLATE.md +246 -246
  197. package/vendor/Server/Skills/rn-lib-add-example/SKILL.md +198 -198
  198. package/vendor/Server/Skills/rn-lib-add-example/references/APP_RENAME_GUIDE.md +40 -40
  199. package/vendor/Server/Skills/rn-lib-add-example/references/SHELL_ADAPTATION_CHECKLIST.md +78 -78
  200. package/vendor/Server/Skills/rn-lib-add-example/references/TEMPLATE_REPO_GUIDE.md +70 -70
  201. package/vendor/Server/Skills/rn-lib-add-example/references/VERIFICATION_GUIDE.md +47 -47
  202. package/vendor/Server/Skills/rn-lib-add-example/scripts/create-example.cjs +252 -252
  203. package/vendor/Server/Skills/rn-lib-update-doc/SKILL.md +176 -176
  204. package/vendor/Server/Skills/rn-lib-update-doc/config.json +5 -5
  205. package/vendor/Server/Skills/rn-lib-update-doc/references/changelog-template.md +35 -35
  206. package/vendor/Server/Skills/rn-lib-update-doc/references/format-diff.md +137 -137
  207. package/vendor/Server/Skills/rn-lib-update-doc/references/link-registry.json +33 -33
  208. package/vendor/Server/Skills/rn-lib-update-doc/references/model-0.4.2-overview.md +40 -40
  209. package/vendor/Server/Skills/rn-lib-update-doc/references/model-0.4.2.md +429 -429
  210. package/vendor/Server/Skills/rn-lib-update-doc/scripts/detect-readme-format.cjs +105 -105
  211. package/vendor/Server/Skills/rn-lib-update-doc/scripts/find-usage-doc.cjs +90 -90
  212. package/vendor/Server/Skills/rn-lib-update-doc/scripts/link-verify.cjs +213 -213
  213. package/vendor/Server/Skills/rn-lib-update-doc/scripts/normalize-changelog.cjs +73 -73
  214. package/vendor/Server/Skills/rn-lib-update-doc/scripts/normalize-package-meta.cjs +258 -258
  215. package/vendor/Server/Skills/rn-lib-update-doc/scripts/normalize-readme-links.cjs +341 -341
  216. package/vendor/Server/Skills/rn-lib-update-doc/scripts/resolve-usage-docs.cjs +67 -67
  217. package/vendor/Server/Skills/rn-library-consistency-check/SKILL.md +299 -299
  218. package/vendor/Server/Skills/rn-library-consistency-check/assets/REPORT_TEMPLATE.md +293 -293
  219. package/vendor/Server/Skills/rn-library-consistency-check/references/ARKTS_COMPONENT_CHECK_GUIDE.md +857 -857
  220. package/vendor/Server/Skills/rn-library-consistency-check/references/ARKTS_TURBOMODULE_CHECK_GUIDE.md +856 -856
  221. package/vendor/Server/Skills/rn-library-consistency-check/references/CHECKLIST.md +157 -157
  222. package/vendor/Server/Skills/rn-library-consistency-check/references/CPP_COMPONENT_CHECK_GUIDE.md +801 -801
  223. package/vendor/Server/Skills/rn-library-consistency-check/references/CPP_TURBOMODULE_CHECK_GUIDE.md +683 -683
  224. package/vendor/Server/Skills/rn-library-consistency-check/references/FUNCTION_COMPARISON_GUIDE.md +233 -233
  225. package/vendor/Server/Skills/rn-library-consistency-check/references/NATIVE_COMPONENT_IDENTIFICATION.md +1373 -1373
  226. package/vendor/Server/Skills/rn-library-consistency-check/references/SCORING_STANDARD.md +220 -220
  227. package/vendor/Server/Skills/rn-library-consistency-check/scripts/validate-skill.py +245 -245
  228. package/vendor/Server/Skills/rn-library-documentation-quality-check/SKILL.md +263 -263
  229. package/vendor/Server/Skills/rn-library-documentation-quality-check/assets/MODEL.md +414 -414
  230. package/vendor/Server/Skills/rn-library-documentation-quality-check/assets/MODEL_en.md +413 -413
  231. package/vendor/Server/Skills/rn-library-documentation-quality-check/assets/REPORT_TEMPLATE.md +311 -311
  232. package/vendor/Server/Skills/rn-library-documentation-quality-check/references/CHECK_CHECKLIST.md +355 -355
  233. package/vendor/Server/Skills/rn-library-documentation-quality-check/references/CROSS_VALIDATION_GUIDE.md +115 -115
  234. package/vendor/Server/Skills/rn-library-documentation-quality-check/references/DOC_COLLECTION_GUIDE.md +139 -139
  235. package/vendor/Server/Skills/rn-library-documentation-quality-check/references/EXAMPLES.md +586 -586
  236. package/vendor/Server/Skills/rn-library-documentation-quality-check/references/GOTCHAS.md +57 -57
  237. package/vendor/Server/Skills/rn-library-documentation-quality-check/scripts/validate-skill.py +259 -259
  238. package/vendor/Server/Skills/rn-library-interface-analyzer/SKILL.md +301 -301
  239. package/vendor/Server/Skills/rn-library-interface-analyzer/assets/REPORT_TEMPLATE.md +148 -148
  240. package/vendor/Server/Skills/rn-library-interface-analyzer/references/DOC_ANNOTATION_GUIDE.md +118 -118
  241. package/vendor/Server/Skills/rn-library-interface-analyzer/references/SCRIPT_USAGE_GUIDE.md +168 -168
  242. package/vendor/Server/Skills/rn-library-interface-analyzer/references/URL_PARSING_RULES.md +176 -176
  243. package/vendor/Server/Skills/rn-library-issue-generalizer/SKILL.md +274 -274
  244. package/vendor/Server/Skills/rn-library-issue-generalizer/assets/case-template.md +45 -45
  245. package/vendor/Server/Skills/rn-library-issue-generalizer/assets/rule-template.md +25 -25
  246. package/vendor/Server/Skills/rn-library-issue-generalizer/references/PR_FETCH_GUIDE.md +61 -61
  247. package/vendor/Server/Skills/rn-library-issue-generalizer/references/RULE_TEMPLATE_GUIDE.md +165 -165
  248. package/vendor/Server/Skills/rn-library-issue-generalizer/scripts/fetch-pr-info.py +239 -239
  249. package/vendor/Server/Skills/rn-library-known-issue-check/SKILL.md +263 -263
  250. package/vendor/Server/Skills/rn-library-known-issue-check/assets/REPORT_TEMPLATE.md +96 -96
  251. package/vendor/Server/Skills/rn-library-known-issue-check/references/COMPLIANCE_CHECKLIST.md +57 -57
  252. package/vendor/Server/Skills/rn-library-known-issue-check/references/INSPECTION_GUIDE.md +99 -99
  253. package/vendor/Server/Skills/rn-library-known-issue-check/references/PRESCREEN_GUIDE.md +64 -64
  254. package/vendor/Server/Skills/rn-library-known-issue-check/references/SOURCE_PREP_GUIDE.md +74 -74
  255. package/vendor/Server/Skills/rn-library-known-issue-check/scripts/validate-known-issue-report.py +164 -164
  256. package/vendor/Server/Skills/rn-library-memory-leak-check/SKILL.md +356 -339
  257. package/vendor/Server/Skills/rn-library-memory-leak-check/assets/REPORT_TEMPLATE.md +85 -83
  258. package/vendor/Server/Skills/rn-library-memory-leak-check/references/CHECKLIST.md +905 -859
  259. package/vendor/Server/Skills/rn-library-memory-leak-check/references/HARMONY_CODE_PATTERNS.md +124 -122
  260. package/vendor/Server/Skills/rn-library-memory-leak-check/scripts/scan_harmony_code.py +287 -279
  261. package/vendor/Server/Skills/rn-library-stability-check/SKILL.md +273 -273
  262. package/vendor/Server/Skills/rn-library-stability-check/assets/REPORT_TEMPLATE.md +129 -129
  263. package/vendor/Server/Skills/rn-library-stability-check/references/CHECKLIST.md +581 -581
  264. package/vendor/Server/Skills/rn-library-stability-check/references/SCORING_RULES.md +138 -138
  265. package/vendor/Server/Skills/rn-library-stability-check/scripts/scan-harmony-code.py +168 -168
  266. package/vendor/Server/Skills/rn-library-test-coverage-check/SKILL.md +526 -506
  267. package/vendor/Server/Skills/rn-library-test-coverage-check/references/CHECKLIST.md +321 -280
  268. package/vendor/Server/Skills/rn-library-test-coverage-check/references/DEMO_LOCATION.md +221 -221
  269. package/vendor/Server/Skills/rn-library-test-coverage-check/references/REPORT_TEMPLATE.md +220 -209
  270. package/vendor/Server/Skills/rn-library-test-coverage-check/references/SCRIPT_USAGE.md +325 -325
  271. package/vendor/Server/Skills/rn-ohos-xts-generator/SKILL.md +279 -279
  272. package/vendor/Server/Skills/rn-ohos-xts-generator/assets/TEST_FILE_TEMPLATES.md +404 -404
  273. package/vendor/Server/Skills/rn-ohos-xts-generator/references/BUILD_TROUBLESHOOTING.md +185 -185
  274. package/vendor/Server/Skills/rn-ohos-xts-generator/references/BUILD_WORKFLOW.md +265 -265
  275. package/vendor/Server/Skills/rn-ohos-xts-generator/references/COMMON_CONFIG.md +189 -189
  276. package/vendor/Server/Skills/rn-ohos-xts-generator/references/CONFIG_GUIDE.md +203 -203
  277. package/vendor/Server/Skills/rn-ohos-xts-generator/references/COVERAGE_ANALYZER.md +147 -147
  278. package/vendor/Server/Skills/rn-ohos-xts-generator/references/DESIGN_DOC_GUIDE.md +148 -148
  279. package/vendor/Server/Skills/rn-ohos-xts-generator/references/GENERATION_FLOW.md +165 -165
  280. package/vendor/Server/Skills/rn-ohos-xts-generator/references/GENERATION_STRATEGY.md +195 -195
  281. package/vendor/Server/Skills/rn-ohos-xts-generator/references/HARMONY_SCANNER.md +249 -249
  282. package/vendor/Server/Skills/rn-ohos-xts-generator/references/JEST_FRAMEWORK.md +235 -235
  283. package/vendor/Server/Skills/rn-ohos-xts-generator/references/PLATFORM_COMPAT.md +161 -161
  284. package/vendor/Server/Skills/rn-ohos-xts-generator/references/RN_TEST_PATTERNS.md +413 -413
  285. package/vendor/Server/Skills/rn-ohos-xts-generator/references/SOURCE_PARSER.md +235 -235
  286. package/vendor/Server/Skills/rn-ohos-xts-generator/references/TEST_CONVENTIONS.md +245 -245
  287. package/vendor/Server/Skills/rn-ohos-xts-generator/references/TROUBLESHOOTING.md +282 -282
  288. package/vendor/Server/Skills/rn-ohos-xts-generator/references/USAGE_GUIDE.md +251 -251
  289. package/vendor/Server/Skills/rnoh-cptf-api-test/SKILL.md +469 -469
  290. package/vendor/Server/Skills/rnoh-cptf-api-test/assets/CPTF_TEST_TEMPLATE.md +310 -310
  291. package/vendor/Server/Skills/rnoh-cptf-api-test/assets/DEMO_PAGE_TEMPLATE.md +303 -303
  292. package/vendor/Server/Skills/rnoh-cptf-api-test/references/CPTF_TEST_GENERATION_GUIDE.md +421 -421
  293. package/vendor/Server/Skills/rnoh-cptf-api-test/references/DEMO_GENERATION_GUIDE.md +358 -358
  294. package/vendor/Server/Skills/rnoh-cptf-api-test/references/QUALITY_CHECKLIST.md +114 -114
  295. package/vendor/Server/Skills/rnoh-cptf-api-test/references/SPEC_PARSING_GUIDE.md +274 -274
  296. package/vendor/Server/Skills/rnoh-cptf-api-test/scripts/validate-rnoh-cptf-api-test.sh +185 -185
  297. package/vendor/Server/Skills/rnoh-cptf-demo-test/SKILL.md +191 -191
  298. package/vendor/Server/Skills/rnoh-cptf-demo-test/assets/TEST_TEMPLATE.md +216 -216
  299. package/vendor/Server/Skills/rnoh-cptf-demo-test/references/CPTF_SETUP.md +107 -107
  300. package/vendor/Server/Skills/rnoh-cptf-demo-test/references/DEMO_ANALYSIS.md +120 -120
  301. package/vendor/Server/Skills/rnoh-cptf-demo-test/scripts/validate-rnoh-cptf-test.sh +108 -108
  302. package/vendor/Server/Skills/rnoh-cptf-test-run/SKILL.md +237 -237
  303. package/vendor/Server/Skills/rnoh-cptf-test-run/references/DEBUG_PLAYBOOK.md +261 -261
  304. package/vendor/Server/Skills/rnoh-cptf-test-run/scripts/validate-run-env.sh +112 -112
  305. package/vendor/Server/Skills/rnoh-lib-code-adapt/SKILL.md +497 -497
  306. package/vendor/Server/Skills/rnoh-lib-code-adapt/references/adapt-checklist.md +270 -270
  307. package/vendor/Server/Skills/rnoh-lib-code-adapt/references/api-migration-checklist.md +164 -164
  308. package/vendor/Server/Skills/rnoh-lib-code-adapt/references/common-compile-errors.md +556 -556
  309. package/vendor/Server/Skills/rnoh-lib-code-adapt/references/deveco-cli-tools.md +459 -459
  310. package/vendor/Server/Skills/rnoh-lib-code-adapt/references/edge-cases.md +65 -65
  311. package/vendor/Server/Skills/rnoh-lib-code-adapt/references/har-tgz-build.md +512 -512
  312. package/vendor/Server/Skills/rnoh-lib-code-adapt/references/version-compat-matrix.md +132 -132
  313. package/vendor/Server/Skills/rnoh-lib-code-adapt/scripts/auto-integrate.sh +424 -424
  314. package/vendor/Server/Skills/rnoh-lib-demo-coverage/SKILL.md +137 -137
  315. package/vendor/Server/Skills/rnoh-lib-demo-create/SKILL.md +306 -306
  316. package/vendor/Server/Skills/rnoh-lib-demo-create/assets/DEMO_PAGE_TEMPLATE.md +485 -485
  317. package/vendor/Server/Skills/rnoh-lib-demo-create/assets/INDEX_TEMPLATE.md +172 -172
  318. package/vendor/Server/Skills/rnoh-lib-demo-create/references/DEMO_CODE_RULES.md +422 -422
  319. package/vendor/Server/Skills/rnoh-lib-demo-create/references/QUALITY_CHECKLIST.md +95 -95
  320. package/vendor/Server/Skills/rnoh-lib-demo-create/references/SOURCE_EXPLORATION.md +169 -169
  321. package/vendor/Server/Skills/rnoh-lib-demo-doc/SKILL.md +554 -554
  322. package/vendor/Server/Skills/rnoh-lib-demo-doc/assets/DEMO_DOC_TEMPLATE.md +106 -106
  323. package/vendor/Server/Skills/rnoh-lib-demo-doc/references/COVERAGE_INCREMENTAL.md +216 -216
  324. package/vendor/Server/Skills/rnoh-lib-demo-doc/references/DEMO_DESIGN_RULES.md +413 -413
  325. package/vendor/Server/Skills/rnoh-lib-demo-doc/references/OUTPUT_FORMAT.md +269 -269
  326. package/vendor/Server/Skills/rnoh-lib-demo-doc/references/QUALITY_CHECKLIST.md +98 -98
  327. package/vendor/Server/Skills/rnoh-lib-func-test/SKILL.md +337 -337
  328. package/vendor/Server/Skills/rnoh-lib-rnt-search/SKILL.md +722 -722
  329. package/vendor/Server/Skills/rnoh-lib-rnt-search/assets/PORT_REPORT_TEMPLATE.md +219 -219
  330. package/vendor/Server/Skills/rnoh-lib-rnt-search/references/DEMO_PORTING_GUIDE.md +345 -345
  331. package/vendor/Server/Skills/rnoh-lib-rnt-search/references/PORT_VERIFICATION.md +269 -269
  332. package/vendor/Server/Skills/rnoh-lib-rnt-search/references/RNT_PROJECT_STRUCTURE.md +173 -173
  333. package/vendor/Server/Skills/rnoh-lib-rnt-search/scripts/analyze-demo-deps.cjs +267 -267
  334. package/vendor/Server/Skills/rnoh-lib-rnt-search/scripts/detect-rnoh-version.cjs +174 -174
  335. package/vendor/Server/Skills/rnoh-lib-rnt-search/scripts/find-rnt-demo.sh +111 -111
  336. package/vendor/Server/Skills/rnoh-lib-rnt-search/scripts/port-demo.sh +186 -186
  337. package/vendor/Server/Skills/rnoh-lib-rnt-search/scripts/read-lib-meta.cjs +150 -150
  338. package/vendor/Server/Skills/rnoh-lib-test-align/SKILL.md +296 -296
  339. package/vendor/Server/Skills/rnoh-lib-test-align/references/jest-troubleshooting.md +371 -371
  340. package/vendor/Server/Skills/rnoh-lib-test-align/references/port-test-demo-guide.md +212 -212
  341. package/vendor/Server/Skills/rnoh-lib-test-align/references/runtime-troubleshooting.md +345 -345
  342. package/vendor/Server/Skills/rnoh-lib-test-align/references/unit-test-template.md +223 -223
  343. package/vendor/Server/Skills/rnoh-lib-test-align/scripts/port-test-demo.sh +1053 -1053
  344. package/vendor/Server/Skills/rnoh-lib-version-diff/SKILL.md +295 -255
  345. package/vendor/Server/Skills/rnoh-lib-version-diff/references/OHOS_ADAPTATION.md +82 -77
  346. package/vendor/Server/Skills/rnoh-lib-version-diff/references/OHOS_API_LOOKUP.md +224 -224
  347. package/vendor/Server/Skills/rnoh-lib-version-diff/references/script-reference.md +175 -174
  348. package/vendor/Server/Skills/rnoh-lib-version-diff/scripts/detect-upstream-version.js +967 -967
  349. package/vendor/Server/Skills/rnoh-lib-version-diff/scripts/generate-diff.js +1798 -1798
  350. package/vendor/Server/Skills/rnoh-lib-version-diff/scripts/package.json +5 -5
  351. package/vendor/Server/Skills/rnoh-lib-version-diff/templates/report-template.md +180 -137
  352. package/vendor/Server/Skills/rnoh-lib-xts-coverage/SKILL.md +145 -145
  353. package/vendor/Server/Skills/skill-generator/SKILL.md +365 -365
  354. package/vendor/Server/Skills/skill-generator/assets/SKILL_TEMPLATE.md +183 -183
  355. package/vendor/Server/Skills/skill-generator/references/COMPLIANCE_CHECKLIST.md +82 -82
  356. package/vendor/Server/Skills/skill-generator/references/KB_QUERY_GUIDE.md +110 -110
  357. package/vendor/Server/Skills/skill-generator/references/MCP_TOOL_DISCOVERY.md +122 -122
  358. package/vendor/Server/Skills/skill-generator/references/SKILL_SPEC.md +121 -121
  359. package/vendor/Server/Skills/skill-generator/scripts/validate-skill.cjs +259 -259
  360. package/vendor/Server/Skills/task-analysis/SKILL.md +234 -234
  361. package/vendor/Server/Skills/task-analysis/assets/OUTPUT_EXAMPLES.md +160 -160
  362. package/vendor/Server/Skills/task-analysis/references/ACCEPTANCE_CRITERIA_GUIDE.md +190 -190
  363. package/vendor/Server/Skills/task-analysis/references/ANALYSIS_SCHEMA.md +224 -224
  364. package/vendor/Server/Skills/task-analysis/references/INTEGRATION_CONTEXT.md +162 -162
  365. package/vendor/Server/Skills/task-analysis/references/LESSONS_LEARNED.md +55 -55
  366. package/vendor/Server/Skills/task-analysis/references/REPLAN_CONTEXT_GUIDE.md +126 -126
  367. package/vendor/Server/Skills/task-analysis/scripts/validate-analysis-output.sh +256 -256
  368. package/vendor/Server/Skills/task-decomposition/SKILL.md +528 -528
  369. package/vendor/Server/Skills/task-decomposition/assets/OUTPUT_EXAMPLES.md +451 -451
  370. package/vendor/Server/Skills/task-decomposition/references/ACCEPTANCE_CRITERIA_GUIDE.md +179 -179
  371. package/vendor/Server/Skills/task-decomposition/references/DEPENDENCY_GRAPH_GUIDE.md +152 -152
  372. package/vendor/Server/Skills/task-decomposition/references/INTEGRATION_CONTEXT.md +175 -175
  373. package/vendor/Server/Skills/task-decomposition/references/REPLAN_CONTEXT_GUIDE.md +195 -195
  374. package/vendor/Server/Skills/task-decomposition/references/TASK_DECOMPOSITION_TEMPLATES.md +226 -226
  375. package/vendor/Server/Skills/task-decomposition/scripts/list-available-skills.cjs +263 -263
  376. package/vendor/Server/Skills/task-decomposition/scripts/skill-blacklist.json +29 -29
  377. package/vendor/Server/Skills/task-decomposition/scripts/validate-decomposition-output.cjs +376 -376
  378. package/vendor/Server/Skills/task-decomposition/scripts/validate-param-consistency.cjs +259 -259
  379. package/vendor/Server/Skills/task-decomposition/scripts/validate-specified-skills.cjs +216 -216
  380. package/vendor/Server/Skills/task-decomposition/scripts/validate-step-graph.cjs +424 -424
  381. package/vendor/Server/Skills/tpc-cpp-check/SKILL.md +427 -427
  382. package/vendor/Server/Skills/tpc-cpp-check/references/CHECKLIST.md +214 -214
  383. package/vendor/Server/Skills/tpc-cpp-check/references/HPKBUILD_GUIDE.md +235 -235
  384. package/vendor/Server/Skills/tpc-cpp-check/references/REPORT_TEMPLATE.md +250 -250
  385. package/vendor/Server/TestCheck/TestCheck.js +775 -775
  386. package/vendor/Server/TestCheck/index.js +11 -11
  387. package/vendor/Server/library/McpToolGuide.js +280 -280
  388. package/vendor/Server/library/StatsService.js +126 -126
  389. package/vendor/Server/library/cstTime.js +16 -16
  390. package/vendor/VERSION +3 -3
  391. package/workflows/pr-check.mjs +105 -105
  392. package/workflows/pr-push.mjs +193 -0
  393. package/vendor/Server/Agent/SkillSelector/types.js +0 -63
@@ -1,859 +1,905 @@
1
- # RN 鸿蒙化代码内存泄露检视清单
2
-
3
- 本文档列出 RN 三方库鸿蒙化代码内存泄露检视的详细检查项、正例/反例代码示例和扣分细则,作为 SKILL.md Phase 1-5 的详细参考。
4
-
5
- > ⚠️ **核心定位**:本清单**仅检视鸿蒙化代码**,不检视 RN JS 层或非鸿蒙化部分的代码。
6
- >
7
- > **重要**:目录名称不固定,需通过源码扫描识别鸿蒙化代码特征(如 `@ohos` 导入、TurboModule 实现、NAPI 函数等),而非仅依赖目录名称。
8
-
9
- **相关参考文档**:
10
- - [SKILL.md](../SKILL.md) — 主工作流文件(检视流程概览、Phase 0-5 核心步骤)
11
- - [HARMONY_CODE_PATTERNS.md](HARMONY_CODE_PATTERNS.md) — Phase 0 鸿蒙代码特征识别模式
12
- - [REPORT_TEMPLATE.md](../assets/REPORT_TEMPLATE.md) — 输出报告模板和评分等级对照表
13
-
14
- ---
15
-
16
- ## 1. TurboModule/NativeModule 生命周期管理
17
-
18
- ### 1.1 TurboModule 生命周期绑定
19
-
20
- | 检查项 | 问题描述 | 风险等级 | 扣分 |
21
- |--------|----------|----------|------|
22
- | `initialize` | 资源初始化是否完整 | 高 | 缺失 -5 |
23
- | `destroy` | 是否释放**所有**资源 | **致命** | 未释放 -20 |
24
- | Bridge 事件监听清理 | NativeEventEmitter/DeviceEventEmitter 监听是否全部移除 | 高 | 未移除 -15 |
25
- | Map 数据清理 | 模块级 Map(如 `mConnectedDevices`)是否清空 | 高 | 未清空 -12 |
26
- | 释放顺序 | 先移除监听→停操作→清数据→释放资源 | 中 | 顺序错误 -8 |
27
-
28
- **正确示例**:
29
-
30
- ```typescript
31
- // ❌ 致命错误:destroy 未释放所有资源
32
- class BleTurboModule extends TurboModule {
33
- private mConnectedDevices: Map<string, GattClientDevice> = new Map();
34
- private eventEmitter: NativeEventEmitter;
35
- private scanListener: EventSubscription | null = null;
36
-
37
- destroy(): void {
38
- // 仅清空了部分 Map,遗漏了事件监听和系统资源
39
- this.mConnectedDevices.clear();
40
- // 缺失:NativeEventEmitter 监听未移除
41
- // 缺失:ble.stopBLEScan() 未调用
42
- // 缺失:GattClientDevice 未 close()
43
- }
44
- }
45
-
46
- // ✅ 正确实现
47
- class BleTurboModule extends TurboModule {
48
- private mConnectedDevices: Map<string, GattClientDevice> = new Map();
49
- private eventEmitter: NativeEventEmitter;
50
- private scanListener: EventSubscription | null = null;
51
-
52
- destroy(): void {
53
- // 1. 移除所有 Bridge 事件监听
54
- if (this.scanListener) {
55
- this.scanListener.remove();
56
- this.scanListener = null;
57
- }
58
- this.eventEmitter.removeAllListeners('onBleStateChange');
59
-
60
- // 2. 停止进行中的操作
61
- ble.stopBLEScan();
62
-
63
- // 3. 释放系统资源
64
- for (const device of this.mConnectedDevices.values()) {
65
- device.close(); // 释放 GattClientDevice
66
- }
67
-
68
- // 4. 清空 Map 数据
69
- this.mConnectedDevices.clear();
70
-
71
- // 5. 置空回调引用
72
- this.scanCallback = null;
73
- }
74
- }
75
- ```
76
-
77
- ### 1.2 NativeComponent 生命周期绑定
78
-
79
- | 检查项 | 问题描述 | 风险等级 | 扣分 |
80
- |--------|----------|----------|------|
81
- | `onDropViewInstance` | 视图销毁时是否释放所有关联资源 | **致命** | 未释放 -15 |
82
- | View 级监听器清理 | 视图级事件监听是否在视图销毁时移除 | 高 | 未移除 -12 |
83
- | RCTEventEmitter 引用清除 | 原生侧事件发送器引用是否清除 | | 未清除 -10 |
84
- | 子组件递归释放 | 自定义 View 的子 View 是否递归释放 | | 未释放 -10 |
85
-
86
- **关键检查**:
87
- - `onDropViewInstance` 必须释放视图持有的所有系统资源(Sensor、Location、Timer 等)
88
- - 视图级的 `on()` 监听不应依赖 TurboModule 的 `destroy()` 释放
89
- - **多实例场景**:同一 ViewManager 创建的多个 View 实例必须各自独立清理
90
-
91
- ```typescript
92
- // ❌ 致命错误:onDropViewInstance 未清理视图资源
93
- class MapViewManager extends ViewManager {
94
- onDropViewInstance(view: Component): void {
95
- super.onDropViewInstance(view);
96
- // 缺失:view 持有的 Sensor 监听未注销
97
- // 缺失:view 的 Timer 未清除
98
- }
99
- }
100
-
101
- // ✅ 正确实现
102
- class MapViewManager extends ViewManager {
103
- onDropViewInstance(view: Component): void {
104
- // 1. 注销视图级 Sensor 监听
105
- sensor.off(sensor.SensorId.ACCELEROMETER, view.accelerometerCallback);
106
-
107
- // 2. 清除视图级 Timer
108
- clearInterval(view.updateTimer);
109
-
110
- // 3. 释放视图级资源
111
- if (view.mediaPlayer) {
112
- view.mediaPlayer.release();
113
- }
114
-
115
- // 4. 从 ViewManager 的 Map 中移除视图实例
116
- this.viewInstances.delete(view.getTag());
117
-
118
- super.onDropViewInstance(view);
119
- }
120
- }
121
- ```
122
-
123
- ---
124
-
125
- ## 2. RN Bridge 事件机制
126
-
127
- ### 2.1 NativeEventEmitter 监听管理
128
-
129
- | 检查项 | 问题描述 | 风险等级 | 扣分 |
130
- |--------|----------|----------|------|
131
- | addListener/removeAllListeners | 每个监听是否在适当时机移除 | | 未移除 -15 |
132
- | DeviceEventEmitter 监听泄露 | DeviceEventEmitter.addListener 后是否在 destroy 移除 | 高 | 未移除 -15 |
133
- | RCTEventEmitter 强引用 | 原生侧 RCTEventEmitter 是否持有已销毁 View 引用 | 高 | 持有 -12 |
134
- | 事件发送器生命周期 | EventEmitter 实例是否随 TurboModule 销毁 | | 未销毁 -10 |
135
-
136
- **致命问题示例**:
137
-
138
- ```typescript
139
- // ❌ 致命:每次调用都注册新监听器,destroy 时未移除
140
- class BleTurboModule extends TurboModule {
141
- startScan(): void {
142
- this.deviceEventEmitter.addListener('onDeviceFound', (device) => {
143
- this.handleDeviceFound(device);
144
- });
145
- // ⚠️ 多次调用会注册多个监听器,且 destroy 时未移除!
146
- }
147
- }
148
-
149
- // ✅ 正确实现:使用 EventSubscription 保存监听引用
150
- class BleTurboModule extends TurboModule {
151
- private scanListenerSubscription: EventSubscription | null = null;
152
-
153
- startScan(): void {
154
- // 先移除旧监听
155
- if (this.scanListenerSubscription) {
156
- this.scanListenerSubscription.remove();
157
- }
158
- this.scanListenerSubscription = this.deviceEventEmitter.addListener(
159
- 'onDeviceFound', (device) => this.handleDeviceFound(device)
160
- );
161
- }
162
-
163
- destroy(): void {
164
- if (this.scanListenerSubscription) {
165
- this.scanListenerSubscription.remove();
166
- this.scanListenerSubscription = null;
167
- }
168
- this.eventEmitter.removeAllListeners('onBleStateChange');
169
- }
170
- }
171
- ```
172
-
173
- ### 2.2 RN Bridge 事件监听的常见泄露场景
174
-
175
- ```typescript
176
- // ❌ 场景1:Promise 回调中的监听器泄露
177
- class NetworkTurboModule extends TurboModule {
178
- async waitForConnection(): Promise<boolean> {
179
- return new Promise((resolve) => {
180
- this.eventEmitter.addListener('onNetworkChange', (state) => {
181
- if (state.connected) {
182
- resolve(true);
183
- // ⚠️ resolve 后监听器未移除!
184
- }
185
- });
186
- });
187
- }
188
- }
189
-
190
- // ✅ 正确实现
191
- class NetworkTurboModule extends TurboModule {
192
- private networkListener: EventSubscription | null = null;
193
-
194
- async waitForConnection(): Promise<boolean> {
195
- return new Promise((resolve) => {
196
- this.networkListener = this.eventEmitter.addListener('onNetworkChange', (state) => {
197
- if (state.connected) {
198
- resolve(true);
199
- // 移除监听
200
- if (this.networkListener) {
201
- this.networkListener.remove();
202
- this.networkListener = null;
203
- }
204
- }
205
- });
206
- });
207
- }
208
- }
209
-
210
- // ❌ 场景2:RCTEventEmitter 持有已销毁 View 引用
211
- class CameraViewManager extends ViewManager {
212
- private eventEmitter: RCTEventEmitter;
213
- private viewMap: Map<number, Component> = new Map();
214
-
215
- // 视图销毁后 eventEmitter 仍可能向该 view 发送事件
216
- sendEventToView(viewTag: number, eventName: string, data: Object): void {
217
- this.eventEmitter.emit(eventName, { viewTag, ...data });
218
- // ⚠️ 如果 view 已被 onDropViewInstance 清理,事件仍会发送
219
- }
220
- }
221
-
222
- // ✅ 正确实现:发送事件前检查 view 是否仍存在
223
- class CameraViewManager extends ViewManager {
224
- private eventEmitter: RCTEventEmitter;
225
- private viewMap: Map<number, Component> = new Map();
226
-
227
- sendEventToView(viewTag: number, eventName: string, data: Object): void {
228
- if (!this.viewMap.has(viewTag)) {
229
- return; // 视图已销毁,不再发送事件
230
- }
231
- this.eventEmitter.emit(eventName, { viewTag, ...data });
232
- }
233
-
234
- onDropViewInstance(view: Component): void {
235
- this.viewMap.delete(view.getTag()); // 从 Map 中移除
236
- super.onDropViewInstance(view);
237
- }
238
- }
239
- ```
240
-
241
- ---
242
-
243
- ## 3. 鸿蒙 API 事件订阅管理
244
-
245
- ### 3.1 @ohos 模块事件订阅
246
-
247
- | 检查项 | 问题描述 | 风险等级 | 扣分 |
248
- |--------|----------|----------|------|
249
- | `on/off` 对称性 | 每个 `on()` 是否有对应 `off()` | **致命** | 无对应 -20 |
250
- | 回调参数匹配 | `off()` 是否传入正确的回调函数引用 | 高 | 参数错误 -12 |
251
- | 多次注册风险 | 同一事件是否可能被多次注册监听 | | 存在风险 -15 |
252
- | TurboModule 级 vs View 级监听 | TurboModule 级监听不应在 View 销毁时取消 | | 混淆 -10 |
253
-
254
- **致命问题示例**:
255
-
256
- ```typescript
257
- // ❌ 致命:每次调用都注册新监听器,无对应取消
258
- class BleTurboModule extends TurboModule {
259
- async setNotifyValue(remoteId: string, characteristic: ble.BLECharacteristic): Promise<void> {
260
- const gatt = this.mConnectedDevices.get(remoteId);
261
- gatt.on('BLECharacteristicChange', (data) => {
262
- this.onCharacteristicReceived(remoteId, data);
263
- });
264
- // ⚠️ 每次调用注册新监听器,断开连接时未取消!
265
- }
266
- }
267
-
268
- // ✅ 正确实现:使用 Map 保存回调引用
269
- class BleTurboModule extends TurboModule {
270
- private characteristicCallbacks: Map<string, Function> = new Map();
271
-
272
- async setNotifyValue(remoteId: string, characteristic: ble.BLECharacteristic): Promise<void> {
273
- const gatt = this.mConnectedDevices.get(remoteId);
274
-
275
- // 先取消旧的监听器
276
- const oldCallback = this.characteristicCallbacks.get(remoteId);
277
- if (oldCallback) {
278
- gatt.off('BLECharacteristicChange', oldCallback);
279
- }
280
-
281
- // 注册新的监听器并保存引用
282
- const newCallback = (data) => this.onCharacteristicReceived(remoteId, data);
283
- gatt.on('BLECharacteristicChange', newCallback);
284
- this.characteristicCallbacks.set(remoteId, newCallback);
285
- }
286
-
287
- destroy(): void {
288
- // 释放所有特征变化监听
289
- for (const [remoteId, callback] of this.characteristicCallbacks) {
290
- const gatt = this.mConnectedDevices.get(remoteId);
291
- if (gatt) {
292
- gatt.off('BLECharacteristicChange', callback);
293
- }
294
- }
295
- this.characteristicCallbacks.clear();
296
- }
297
- }
298
- ```
299
-
300
- ### 3.2 特殊系统资源释放
301
-
302
- | 检查项 | 问题描述 | 风险等级 | 扣分 |
303
- |--------|----------|----------|------|
304
- | GattClientDevice.close | BLE Gatt 客户端是否调用 close() | **致命** | 未调用 -20 |
305
- | 扫描状态清理 | `ble.stopBLEScan()` 是否在适当时机调用 | 高 | 未调用 -15 |
306
- | 地理位置监听 | `geoLocationManager.off()` 是否取消 | | 未取消 -15 |
307
- | Sensor 注销 | `sensor.off()` 是否取消 | **致命** | 未注销 -20 |
308
- | 网络状态监听 | `connection.on/off` 是否对称 | 高 | 不对称 -12 |
309
- | 数据库连接未关闭 | rdb 关闭未调用 close | **致命** | 未关闭 -20 |
310
- | Timer 未清除 | setInterval/setTimeout 未在 destroy 时清除 | 高 | 未清除 -12 |
311
-
312
- **致命泄漏场景**:
313
-
314
- ```typescript
315
- // ❌ 致命:GattClientDevice 创建后未释放
316
- class BleTurboModule extends TurboModule {
317
- async scanCallback(device: ble.ScanResult): void {
318
- const gattClient = ble.createGattClientDevice(device.getMac());
319
- const deviceName = await gattClient.getDeviceName();
320
- // ⚠️ gattClient 未调用 close(),每次扫描都泄漏!
321
- }
322
- }
323
-
324
- // ❌ 致命:服务查询创建的 GattClientDevice 未释放
325
- async getServices(remoteId: string): Promise<ble.GattService[]> {
326
- const gattClient = ble.createGattClientDevice(remoteId);
327
- const services = await gattClient.getServices();
328
- return services; // ⚠️ gattClient 未释放
329
- }
330
-
331
- // ✅ 正确实现:使用 try-finally 确保释放
332
- async getServices(remoteId: string): Promise<ble.GattService[]> {
333
- const gattClient = ble.createGattClientDevice(remoteId);
334
- try {
335
- const services = await gattClient.getServices();
336
- return services;
337
- } finally {
338
- gattClient.close(); // 必须释放
339
- }
340
- }
341
- ```
342
-
343
- ---
344
-
345
- ## 4. 闭包与上下文泄露
346
-
347
- ### 4.1 闭包循环引用
348
-
349
- | 检查项 | 问题描述 | 风险等级 | 扣分 |
350
- |--------|----------|----------|------|
351
- | this 捕获 | 箭头函数是否捕获 `this` 引用且未在 destroy 释放 | | 捕获且未释放 -15 |
352
- | 闭包保留 | 闭包是否保留外部变量引用且无法释放 | 高 | 保留且无法释放 -12 |
353
- | RN 回调链泄露 | JS Native JS 回调链中的闭包引用是否正确断开 | 高 | 未断开 -12 |
354
- | 异步回调持有 stale context | setTimeout/Promise 回调中引用已销毁 TurboModule 上下文 | | 引用 -10 |
355
-
356
- **循环引用检测**:
357
-
358
- ```typescript
359
- // ❌ 风险:箭头函数捕获 this,形成循环引用
360
- class BleTurboModule extends TurboModule {
361
- private gatt: ble.GattClientDevice;
362
-
363
- connect(): void {
364
- this.gatt.on('BLEConnectionStateChange', (state) => {
365
- this.onConnectionStateChanged(state); // this 被捕获
366
- });
367
- // 循环链:this.gatt → 回调 → this → this.gatt
368
- }
369
- }
370
-
371
- // ✅ 正确实现:在构造函数中预创建回调,destroy 时清除引用
372
- class BleTurboModule extends TurboModule {
373
- private gatt: ble.GattClientDevice;
374
- private connectionCallback: Function;
375
-
376
- constructor() {
377
- this.connectionCallback = (state: ble.ProfileConnectionState) => {
378
- this.onConnectionStateChanged(state);
379
- };
380
- }
381
-
382
- connect(): void {
383
- this.gatt.on('BLEConnectionStateChange', this.connectionCallback);
384
- }
385
-
386
- destroy(): void {
387
- if (this.gatt) {
388
- this.gatt.off('BLEConnectionStateChange', this.connectionCallback);
389
- }
390
- this.connectionCallback = null; // 清除引用,打破循环
391
- }
392
- }
393
- ```
394
-
395
- ### 4.2 RN 特有回调链泄露
396
-
397
- ```typescript
398
- // ❌ 风险:TurboModule 级回调持有 View 实例引用
399
- class CameraTurboModule extends TurboModule {
400
- private onFrameCallback: ((frameData: Object) => void) | null = null;
401
-
402
- startCamera(viewTag: number): void {
403
- const view = this.viewManager.getViewByTag(viewTag);
404
- this.onFrameCallback = (frameData: Object) => {
405
- view.updateFrame(frameData); // 持有 view 引用
406
- };
407
- // ⚠️ view 销毁后回调仍持有引用
408
- }
409
- }
410
-
411
- // ✅ 正确实现:使用 viewTag 而非 view 引用
412
- class CameraTurboModule extends TurboModule {
413
- private onFrameCallbacks: Map<number, Function> = new Map();
414
-
415
- startCamera(viewTag: number): void {
416
- this.onFrameCallbacks.set(viewTag, (frameData: Object) => {
417
- const view = this.viewManager.getViewByTag(viewTag);
418
- if (view) {
419
- view.updateFrame(frameData); // 按需获取 view
420
- }
421
- });
422
- }
423
-
424
- // View 销毁时移除回调
425
- removeFrameCallback(viewTag: number): void {
426
- this.onFrameCallbacks.delete(viewTag);
427
- }
428
-
429
- destroy(): void {
430
- this.onFrameCallbacks.clear();
431
- }
432
- }
433
- ```
434
-
435
- ### 4.3 Map/Set 数据清理
436
-
437
- | 检查项 | 问题描述 | 风险等级 | 扣分 |
438
- |--------|----------|----------|------|
439
- | Map.clear() | TurboModule 的 Map 数据是否在 destroy 时调用 clear() | | 未清理 -10 |
440
- | View 实例 Map | ViewManager 维护的 View 实例 Map 是否在 onDropViewInstance 删除 | 高 | 未删除 -12 |
441
- | 静态 Map 风险 | 静态 Map 是否无限累积 | **致命** | 累积风险 -15 |
442
- | 缓存上限 | 是否设置缓存大小上限 | | 无上限 -8 |
443
-
444
- **Map 清理检查**:
445
-
446
- ```typescript
447
- // ❌ 问题:ViewManager 的 View 实例 Map 未在 onDropViewInstance 删除
448
- class CameraViewManager extends ViewManager {
449
- private viewInstances: Map<number, CameraView> = new Map();
450
-
451
- createViewInstance(viewTag: number): CameraView {
452
- const view = new CameraView();
453
- this.viewInstances.set(viewTag, view);
454
- return view;
455
- }
456
-
457
- onDropViewInstance(view: Component): void {
458
- super.onDropViewInstance(view);
459
- // ⚠️ viewInstances 中该 viewTag 对应的条目未删除!
460
- }
461
- }
462
-
463
- // ✅ 正确实现
464
- class CameraViewManager extends ViewManager {
465
- private viewInstances: Map<number, CameraView> = new Map();
466
-
467
- createViewInstance(viewTag: number): CameraView {
468
- const view = new CameraView();
469
- this.viewInstances.set(viewTag, view);
470
- return view;
471
- }
472
-
473
- onDropViewInstance(view: Component): void {
474
- const viewTag = view.getTag();
475
- const cameraView = this.viewInstances.get(viewTag);
476
- if (cameraView) {
477
- cameraView.release(); // 释放视图资源
478
- this.viewInstances.delete(viewTag); // 从 Map 中移除
479
- }
480
- super.onDropViewInstance(view);
481
- }
482
- }
483
- ```
484
-
485
- ---
486
-
487
- ## 5. NAPI 引用管理
488
-
489
- ### 5.1 NAPI 引用生命周期
490
-
491
- | 检查项 | 问题描述 | 风险等级 | 扣分 |
492
- |--------|----------|----------|------|
493
- | `napi_ref` 释放 | 是否在 dispose/destroy 时调用 `napi_delete_reference` | **致命** | 未释放 -20 |
494
- | Handle Scope 清理 | 是否正确关闭 `napi_handle_scope` | **致命** | 未关闭 -20 |
495
- | finalize 回调 | `napi_wrap` 是否提供 finalize 回调释放 Native 对象 | **致命** | 缺失 -20 |
496
- | 引用计数平衡 | `napi_create_reference` `napi_delete_reference` 数量是否匹配 | | 不平衡 -18 |
497
- | ArrayBuffer 外部资源泄露 | `napi_create_external` ArrayBuffer finalize 回调缺失 | | 缺失 -15 |
498
- | NAPI 线程安全函数引用泄露 | `napi_create_threadsafe_function` 后未调用 `napi_release_threadsafe_function` | 高 | 未释放 -15 |
499
-
500
- **C++ NAPI 管理示例**:
501
-
502
- ```cpp
503
- // ❌ 致命:NAPI 引用未释放,且缺少 finalize 回调
504
- class NativeTurboModule {
505
- private:
506
- napi_ref callbackRef;
507
-
508
- public:
509
- void registerCallback(napi_env env, napi_value callback) {
510
- napi_create_reference(env, callback, 1, &this->callbackRef);
511
- // 缺少释放逻辑!
512
- // 缺少 finalize 回调!
513
- }
514
- };
515
-
516
- // ✅ 正确实现
517
- class NativeTurboModule {
518
- private:
519
- napi_ref callbackRef = nullptr;
520
-
521
- public:
522
- void registerCallback(napi_env env, napi_value callback) {
523
- if (this->callbackRef) {
524
- napi_delete_reference(env, this->callbackRef);
525
- }
526
- napi_create_reference(env, callback, 1, &this->callbackRef);
527
- }
528
-
529
- void destroy(napi_env env) {
530
- if (this->callbackRef) {
531
- napi_delete_reference(env, this->callbackRef);
532
- this->callbackRef = nullptr;
533
- }
534
- }
535
- };
536
-
537
- // ✅ 正确实现:napi_wrap 带 finalize 回调
538
- void NativeObjectFinalize(napi_env env, void* nativeObj, void* hint) {
539
- auto* obj = static_cast<NativeResource*>(nativeObj);
540
- delete obj; // finalize 中释放 Native 对象
541
- }
542
-
543
- napi_status WrapNativeObject(napi_env env, napi_value jsThis, NativeResource* nativeObj) {
544
- napi_ref ref;
545
- return napi_wrap(env, jsThis, nativeObj, NativeObjectFinalize, nullptr, &ref);
546
- }
547
- ```
548
-
549
- ### 5.2 Handle Scope 管理
550
-
551
- ```cpp
552
- // ❌ 致命:Handle Scope 未关闭
553
- napi_value ProcessData(napi_env env, napi_callback_info info) {
554
- napi_handle_scope scope;
555
- napi_open_handle_scope(env, &scope);
556
-
557
- napi_value result;
558
- napi_create_string_utf8(env, "hello", NAPI_AUTO_LENGTH, &result);
559
-
560
- // ⚠️ 缺少 napi_close_handle_scope!
561
- return result;
562
- }
563
-
564
- // ✅ 正确实现:使用 RAII 或手动关闭
565
- napi_value ProcessData(napi_env env, napi_callback_info info) {
566
- napi_handle_scope scope;
567
- napi_open_handle_scope(env, &scope);
568
-
569
- napi_value result;
570
- napi_create_string_utf8(env, "hello", NAPI_AUTO_LENGTH, &result);
571
-
572
- napi_close_handle_scope(env, scope); // 必须关闭
573
- return result;
574
- }
575
- ```
576
-
577
- ---
578
-
579
- ## 6. TurboModule/JSI 绑定内存
580
-
581
- ### 6.1 TurboModule C++ 生命周期管理
582
-
583
- | 检查项 | 问题描述 | 风险等级 | 扣分 |
584
- |--------|----------|----------|------|
585
- | TurboModule C++ 实例释放 | C++ 实例在 JS 对象回收后是否释放 | **致命** | 未释放 -15 |
586
- | JSI HostFunction 泄露 | HostFunction 绑定在 Runtime 销毁时是否释放 | **致命** | 未释放 -15 |
587
- | JSI HostObject 属性回调泄露 | HostObject getter/setter 闭包持有外部引用未清理 | | 未清理 -12 |
588
- | 跨语言回调链泄露 | C++ 层回调引用 JS 函数的双向引用是否正确断开 | | 未断开 -12 |
589
- | Codegen 生成代码资源释放 | Codegen 自动生成的代码中资源释放逻辑是否完整 | | 不完整 -10 |
590
- | TurboModule C++ 与 JS 对象生命周期不一致 | C++ 侧实例比 JS 侧对象存活更久,导致悬垂引用 | **致命** | 不一致 -15 |
591
-
592
- **TurboModule C++ 资源释放示例**:
593
-
594
- ```cpp
595
- // ❌ 致命:TurboModule C++ 实例未在析构时释放资源
596
- class BleTurboModule : public TurboModule {
597
- private:
598
- std::map<std::string, GattClient*> mConnectedDevices;
599
- napi_ref mJsCallbackRef;
600
-
601
- public:
602
- ~BleTurboModule() {
603
- // ⚠️ 未释放 mConnectedDevices 中的 GattClient
604
- // ⚠️ 未释放 mJsCallbackRef
605
- }
606
- };
607
-
608
- // ✅ 正确实现
609
- class BleTurboModule : public TurboModule {
610
- private:
611
- std::map<std::string, GattClient*> mConnectedDevices;
612
- napi_ref mJsCallbackRef = nullptr;
613
-
614
- public:
615
- ~BleTurboModule() {
616
- // 1. 释放所有 GattClient
617
- for (auto& [id, client] : mConnectedDevices) {
618
- delete client;
619
- }
620
- mConnectedDevices.clear();
621
-
622
- // 2. 释放 NAPI 引用(注意:需要 env,通常在 destroy 中处理)
623
- }
624
-
625
- void destroy(napi_env env) {
626
- // 释放 NAPI 引用
627
- if (mJsCallbackRef) {
628
- napi_delete_reference(env, mJsCallbackRef);
629
- mJsCallbackRef = nullptr;
630
- }
631
- }
632
- };
633
- ```
634
-
635
- ### 6.2 JSI HostFunction/HostObject 管理
636
-
637
- ```cpp
638
- // ❌ 风险:HostFunction 闭包持有外部引用
639
- class MyHostObject : public jsi::HostObject {
640
- private:
641
- jsi::Function mCallback; // 持有 JS 函数引用
642
-
643
- public:
644
- jsi::Value get(jsi::Runtime& rt, const jsi::PropNameID& name) override {
645
- if (name.utf8(rt) == "callback") {
646
- return mCallback; // 返回 JS 函数
647
- }
648
- // ⚠️ Runtime 销毁时 mCallback 未释放
649
- }
650
- };
651
-
652
- // ✅ 正确实现:在 Runtime 销毁时清理
653
- class MyHostObject : public jsi::HostObject {
654
- private:
655
- std::optional<jsi::Function> mCallback;
656
-
657
- public:
658
- jsi::Value get(jsi::Runtime& rt, const jsi::PropNameID& name) override {
659
- if (name.utf8(rt) == "callback" && mCallback.has_value()) {
660
- return mCallback.value();
661
- }
662
- return jsi::Value::undefined();
663
- }
664
-
665
- // Runtime 销毁时由 TurboModule 的 destroy 调用
666
- void invalidate() {
667
- mCallback.reset(); // 清除 JS 函数引用
668
- }
669
- };
670
- ```
671
-
672
- ---
673
-
674
- ## 7. C/C++ 层内存泄露
675
-
676
- ### 7.1 Native 内存分配释放
677
-
678
- | 检查项 | 问题描述 | 风险等级 | 扣分 |
679
- |--------|----------|----------|------|
680
- | malloc/free 匹配 | 每个 `malloc` 是否有对应 `free` | **致命** | 不匹配 -15 |
681
- | new/delete 匹配 | 每个 `new` 是否有对应 `delete` | **致命** | 不匹配 -15 |
682
- | new[]/delete[] 匹配 | `new[]` 是否使用 `delete[]` 释放 | **致命** | 误用 -15 |
683
- | 智能指针循环引用 | `shared_ptr` 循环引用导致引用计数永不为零 | **致命** | 循环引用 -15 |
684
- | 异常路径释放 | 异常发生时是否释放已分配内存 | | 未释放 -12 |
685
- | RAII 模式 | 是否优先使用智能指针 | | 未使用 -10 |
686
- | 全局/静态缓冲区无限增长 | 全局 vector/map 无上限增长 | 高 | 无清理 -12 |
687
-
688
- **RAII 推荐模式**:
689
-
690
- ```cpp
691
- // ✅ 使用智能指针自动管理
692
- #include <memory>
693
-
694
- void processData() {
695
- std::unique_ptr<Buffer> buffer = std::make_unique<Buffer>(1024);
696
- // 自动释放,无需手动 delete
697
-
698
- // 或使用 std::shared_ptr 处理共享所有权
699
- std::shared_ptr<Resource> resource = std::make_shared<Resource>();
700
- }
701
-
702
- // ✅ RAII wrapper 模式
703
- class BufferGuard {
704
- private:
705
- void* buf;
706
- public:
707
- BufferGuard(size_t size) : buf(malloc(size)) {}
708
- ~BufferGuard() {
709
- if (buf) free(buf); // 自动释放
710
- }
711
- // 禁止拷贝
712
- BufferGuard(const BufferGuard&) = delete;
713
- BufferGuard& operator=(const BufferGuard&) = delete;
714
- };
715
- ```
716
-
717
- **智能指针循环引用检测**:
718
-
719
- ```cpp
720
- // ❌ 致命:shared_ptr 循环引用
721
- class TurboModuleImpl;
722
- class CallbackHolder : public std::enable_shared_from_this<CallbackHolder> {
723
- public:
724
- std::shared_ptr<TurboModuleImpl> module; // 持有 module 引用
725
- };
726
-
727
- class TurboModuleImpl : public std::enable_shared_from_this<TurboModuleImpl> {
728
- public:
729
- std::shared_ptr<CallbackHolder> callback; // 持有 callback 引用
730
- // 循环链:module ↔ callback,引用计数永不为零!
731
- };
732
-
733
- // ✅ 正确实现:使用 weak_ptr 打破循环
734
- class CallbackHolder {
735
- public:
736
- std::weak_ptr<TurboModuleImpl> module; // 弱引用,不增加引用计数
737
- };
738
-
739
- class TurboModuleImpl : public std::enable_shared_from_this<TurboModuleImpl> {
740
- public:
741
- std::shared_ptr<CallbackHolder> callback;
742
- };
743
- ```
744
-
745
- ---
746
-
747
- ## 8. RN 鸿蒙化特有内存风险
748
-
749
- ### 8.1 跨语言生命周期不一致
750
-
751
- | 检查项 | 问题描述 | 风险等级 | 扣分 |
752
- |--------|----------|----------|------|
753
- | ArkTS on/C++ resource 不对称释放 | ArkTS 层调用 off() 但 C++ 层未释放对应资源,或反过来 | **致命** | 不对称 -15 |
754
- | Native 对象生命周期不一致 | C++ Native 对象与 ArkTS 包装对象生命周期不一致 | **致命** | 不一致 -15 |
755
- | TurboModule View 生命周期混淆 | TurboModule 级资源不应在 View 销毁时释放,反之亦然 | | 混淆 -12 |
756
- | RN Bridge 消息队列累积 | 原生模块与 JS 之间的消息队列在模块销毁时未清空 | | 未清空 -10 |
757
-
758
- **跨语言释放不对称示例**:
759
-
760
- ```typescript
761
- // ❌ 致命:ArkTS 层 off() 但 C++ 层未释放资源
762
- class BleTurboModule extends TurboModule {
763
- disconnect(remoteId: string): void {
764
- const gatt = this.mConnectedDevices.get(remoteId);
765
- gatt.off('BLEConnectionStateChange', this.connectionCallback);
766
- this.mConnectedDevices.delete(remoteId);
767
- // ⚠️ ArkTS 层取消了监听并从 Map 删除,但 C++ 层的 GattClient 未释放
768
- }
769
- }
770
-
771
- // ✅ 正确实现:确保 C++ 层也释放对应资源
772
- disconnect(remoteId: string): void {
773
- const gatt = this.mConnectedDevices.get(remoteId);
774
- if (gatt) {
775
- gatt.off('BLEConnectionStateChange', this.connectionCallback);
776
- gatt.close(); // 确保 C++ 层资源也释放
777
- }
778
- this.mConnectedDevices.delete(remoteId);
779
- }
780
- ```
781
-
782
- ### 8.2 Spec Codegen 生成代码
783
-
784
- ```typescript
785
- // ❌ 风险:Codegen 生成的 TurboModule 代码中未实现资源释放
786
- // Codegen 生成的接口:
787
- interface NativeBleModule extends TurboModule {
788
- startScan(): Promise<void>;
789
- stopScan(): Promise<void>;
790
- // ⚠️ 没有 destroy/invalidate 方法来释放资源
791
- }
792
-
793
- // ✅ 正确做法:在 Codegen 生成的接口基础上,补充资源释放逻辑
794
- class BleTurboModuleImpl extends TurboModule implements NativeBleModuleSpec {
795
- // 实现 Codegen 接口方法
796
- startScan(): Promise<void> { ... }
797
- stopScan(): Promise<void> { ... }
798
-
799
- // 补充资源释放方法(在 TurboModule destroy 时调用)
800
- destroy(): void {
801
- // 释放 Codegen 未管理的资源
802
- this.cleanupResources();
803
- }
804
- }
805
- ```
806
-
807
- ---
808
-
809
- ## 9. 评分规则汇总
810
-
811
- ### 致命问题(-20 分,最多扣 60 分)
812
-
813
- | 问题类型 | 扣分 | 直接 0 分条件 |
814
- |----------|------|---------------|
815
- | TurboModule destroy 未释放资源 | -20 | 3 个致命问题 |
816
- | onDropViewInstance 未清理 | -15 | ≥ 2 个致命 + ≥ 3 个严重 |
817
- | NAPI 引用未释放 | -20 | 关键路径存在致命 |
818
- | finalize 回调缺失 | -20 | 累计扣分 > 60 |
819
- | Handle Scope 未关闭 | -20 | |
820
- | malloc/free 不匹配 | -15 | |
821
- | new/delete 不匹配 | -15 | |
822
- | 鸿蒙 API 事件订阅无对应取消 | -20 | |
823
- | NativeEventEmitter 监听未移除 | -15 | |
824
- | GattClientDevice 未释放 | -20 | |
825
- | Sensor 未注销 | -20 | |
826
- | 数据库连接未关闭 | -20 | |
827
-
828
- ### 严重问题(-12 分)
829
-
830
- | 问题类型 | 扣分 |
831
- |----------|------|
832
- | Bridge 事件监听未移除 | -15 |
833
- | 闭包循环引用风险 | -15 |
834
- | Map 数据未清理 | -10 |
835
- | View 实例 Map 未删除 | -12 |
836
- | 跨语言回调链泄露 | -12 |
837
- | JSI HostObject 属性回调泄露 | -12 |
838
- | 扫描状态未清理 | -15 |
839
- | Timer 未清除 | -12 |
840
-
841
- ### 中等问题(-8 分)
842
-
843
- | 问题类型 | 扣分 |
844
- |----------|------|
845
- | 回调参数匹配错误 | -12 |
846
- | 释放顺序不当 | -8 |
847
- | 初始化不完整 | -5 |
848
- | TurboModule 级 vs View 级监听混淆 | -10 |
849
- | Codegen 生成代码资源释放不完整 | -10 |
850
- | RN Bridge 消息队列未清空 | -10 |
851
-
852
- ### 轻微问题(-5 分)
853
-
854
- | 问题类型 | 扣分 |
855
- |----------|------|
856
- | 订阅状态未追踪 | -8 |
857
- | 缓存无上限 | -8 |
858
- | RAII 未使用 | -10 |
859
- | 事件发送器未随 TurboModule 销毁 | -10 |
1
+ # RN 鸿蒙化代码内存泄露检视清单
2
+
3
+ 本文档列出 RN 三方库鸿蒙化代码内存泄露检视的详细检查项、正例/反例代码示例和扣分细则,作为 SKILL.md Phase 1-5 的详细参考。
4
+
5
+ > ⚠️ **核心定位**:本清单**仅检视鸿蒙化代码**,不检视 RN JS 层或非鸿蒙化部分的代码。
6
+ >
7
+ > **重要**:目录名称不固定,需通过源码扫描识别鸿蒙化代码特征(如 `@ohos` 导入、TurboModule 实现、NAPI 函数等),而非仅依赖目录名称。
8
+
9
+ **相关参考文档**:
10
+ - [SKILL.md](../SKILL.md) — 主工作流文件(检视流程概览、Phase 0-5 核心步骤)
11
+ - [HARMONY_CODE_PATTERNS.md](HARMONY_CODE_PATTERNS.md) — Phase 0 鸿蒙代码特征识别模式
12
+ - [REPORT_TEMPLATE.md](../assets/REPORT_TEMPLATE.md) — 输出报告模板和评分等级对照表
13
+
14
+ ---
15
+
16
+ ## 1. TurboModule/NativeModule 生命周期管理
17
+
18
+ ### 1.1 TurboModule 生命周期绑定
19
+
20
+ | 检查项 | 问题描述 | 风险等级 | 扣分 |
21
+ |--------|----------|----------|------|
22
+ | `initialize` | 资源初始化是否完整 | 高 | 缺失 -5 |
23
+ | `destroy` | 是否释放**所有**资源 | **致命** | 未释放 -20 |
24
+ | Bridge 事件监听清理 | NativeEventEmitter/DeviceEventEmitter 监听是否全部移除 | 高 | 未移除 -15 |
25
+ | Map 数据清理 | 模块级 Map(如 `mConnectedDevices`)是否清空 | 高 | 未清空 -12 |
26
+ | 释放顺序 | 先移除监听→停操作→清数据→释放资源 | 中 | 顺序错误 -8 |
27
+
28
+ > ⚠️ **无状态模块豁免**:`destroy` 未释放扣 20 分的前提是模块**持有需要释放的资源**(事件监听/系统资源句柄/Map/Set 容器/回调引用/实例字段)。零状态模块未重写 `__onDestroy__`/`destroy` 属正确最小化设计,**不扣分**,报告中注明"无泄露面"。适用豁免前必须先完成[第 10 节零泄露面穷尽验证](#10-零泄露面穷尽验证模式集无状态库确认)。
29
+
30
+ **正确示例**:
31
+
32
+ ```typescript
33
+ // 致命错误:destroy 未释放所有资源
34
+ class BleTurboModule extends TurboModule {
35
+ private mConnectedDevices: Map<string, GattClientDevice> = new Map();
36
+ private eventEmitter: NativeEventEmitter;
37
+ private scanListener: EventSubscription | null = null;
38
+
39
+ destroy(): void {
40
+ // 仅清空了部分 Map,遗漏了事件监听和系统资源
41
+ this.mConnectedDevices.clear();
42
+ // 缺失:NativeEventEmitter 监听未移除
43
+ // 缺失:ble.stopBLEScan() 未调用
44
+ // 缺失:GattClientDevice 未 close()
45
+ }
46
+ }
47
+
48
+ // 正确实现
49
+ class BleTurboModule extends TurboModule {
50
+ private mConnectedDevices: Map<string, GattClientDevice> = new Map();
51
+ private eventEmitter: NativeEventEmitter;
52
+ private scanListener: EventSubscription | null = null;
53
+
54
+ destroy(): void {
55
+ // 1. 移除所有 Bridge 事件监听
56
+ if (this.scanListener) {
57
+ this.scanListener.remove();
58
+ this.scanListener = null;
59
+ }
60
+ this.eventEmitter.removeAllListeners('onBleStateChange');
61
+
62
+ // 2. 停止进行中的操作
63
+ ble.stopBLEScan();
64
+
65
+ // 3. 释放系统资源
66
+ for (const device of this.mConnectedDevices.values()) {
67
+ device.close(); // 释放 GattClientDevice
68
+ }
69
+
70
+ // 4. 清空 Map 数据
71
+ this.mConnectedDevices.clear();
72
+
73
+ // 5. 置空回调引用
74
+ this.scanCallback = null;
75
+ }
76
+ }
77
+ ```
78
+
79
+ ### 1.2 NativeComponent 生命周期绑定
80
+
81
+ | 检查项 | 问题描述 | 风险等级 | 扣分 |
82
+ |--------|----------|----------|------|
83
+ | `onDropViewInstance` | 视图销毁时是否释放所有关联资源 | **致命** | 未释放 -15 |
84
+ | View 级监听器清理 | 视图级事件监听是否在视图销毁时移除 | | 未移除 -12 |
85
+ | RCTEventEmitter 引用清除 | 原生侧事件发送器引用是否清除 | 高 | 未清除 -10 |
86
+ | 子组件递归释放 | 自定义 View 的子 View 是否递归释放 | 中 | 未释放 -10 |
87
+
88
+ **关键检查**:
89
+ - `onDropViewInstance` 必须释放视图持有的所有系统资源(Sensor、Location、Timer 等)
90
+ - 视图级的 `on()` 监听不应依赖 TurboModule 的 `destroy()` 释放
91
+ - **多实例场景**:同一 ViewManager 创建的多个 View 实例必须各自独立清理
92
+
93
+ ```typescript
94
+ // 致命错误:onDropViewInstance 未清理视图资源
95
+ class MapViewManager extends ViewManager {
96
+ onDropViewInstance(view: Component): void {
97
+ super.onDropViewInstance(view);
98
+ // 缺失:view 持有的 Sensor 监听未注销
99
+ // 缺失:view 的 Timer 未清除
100
+ }
101
+ }
102
+
103
+ // 正确实现
104
+ class MapViewManager extends ViewManager {
105
+ onDropViewInstance(view: Component): void {
106
+ // 1. 注销视图级 Sensor 监听
107
+ sensor.off(sensor.SensorId.ACCELEROMETER, view.accelerometerCallback);
108
+
109
+ // 2. 清除视图级 Timer
110
+ clearInterval(view.updateTimer);
111
+
112
+ // 3. 释放视图级资源
113
+ if (view.mediaPlayer) {
114
+ view.mediaPlayer.release();
115
+ }
116
+
117
+ // 4. 从 ViewManager 的 Map 中移除视图实例
118
+ this.viewInstances.delete(view.getTag());
119
+
120
+ super.onDropViewInstance(view);
121
+ }
122
+ }
123
+ ```
124
+
125
+ ---
126
+
127
+ ## 2. RN Bridge 事件机制
128
+
129
+ ### 2.1 NativeEventEmitter 监听管理
130
+
131
+ | 检查项 | 问题描述 | 风险等级 | 扣分 |
132
+ |--------|----------|----------|------|
133
+ | addListener/removeAllListeners | 每个监听是否在适当时机移除 | 高 | 未移除 -15 |
134
+ | DeviceEventEmitter 监听泄露 | DeviceEventEmitter.addListener 后是否在 destroy 移除 | | 未移除 -15 |
135
+ | RCTEventEmitter 强引用 | 原生侧 RCTEventEmitter 是否持有已销毁 View 引用 | 高 | 持有 -12 |
136
+ | 事件发送器生命周期 | EventEmitter 实例是否随 TurboModule 销毁 | 中 | 未销毁 -10 |
137
+
138
+ **致命问题示例**:
139
+
140
+ ```typescript
141
+ // 致命:每次调用都注册新监听器,destroy 时未移除
142
+ class BleTurboModule extends TurboModule {
143
+ startScan(): void {
144
+ this.deviceEventEmitter.addListener('onDeviceFound', (device) => {
145
+ this.handleDeviceFound(device);
146
+ });
147
+ // ⚠️ 多次调用会注册多个监听器,且 destroy 时未移除!
148
+ }
149
+ }
150
+
151
+ // 正确实现:使用 EventSubscription 保存监听引用
152
+ class BleTurboModule extends TurboModule {
153
+ private scanListenerSubscription: EventSubscription | null = null;
154
+
155
+ startScan(): void {
156
+ // 先移除旧监听
157
+ if (this.scanListenerSubscription) {
158
+ this.scanListenerSubscription.remove();
159
+ }
160
+ this.scanListenerSubscription = this.deviceEventEmitter.addListener(
161
+ 'onDeviceFound', (device) => this.handleDeviceFound(device)
162
+ );
163
+ }
164
+
165
+ destroy(): void {
166
+ if (this.scanListenerSubscription) {
167
+ this.scanListenerSubscription.remove();
168
+ this.scanListenerSubscription = null;
169
+ }
170
+ this.eventEmitter.removeAllListeners('onBleStateChange');
171
+ }
172
+ }
173
+ ```
174
+
175
+ ### 2.2 RN Bridge 事件监听的常见泄露场景
176
+
177
+ ```typescript
178
+ // 场景1:Promise 回调中的监听器泄露
179
+ class NetworkTurboModule extends TurboModule {
180
+ async waitForConnection(): Promise<boolean> {
181
+ return new Promise((resolve) => {
182
+ this.eventEmitter.addListener('onNetworkChange', (state) => {
183
+ if (state.connected) {
184
+ resolve(true);
185
+ // ⚠️ resolve 后监听器未移除!
186
+ }
187
+ });
188
+ });
189
+ }
190
+ }
191
+
192
+ // 正确实现
193
+ class NetworkTurboModule extends TurboModule {
194
+ private networkListener: EventSubscription | null = null;
195
+
196
+ async waitForConnection(): Promise<boolean> {
197
+ return new Promise((resolve) => {
198
+ this.networkListener = this.eventEmitter.addListener('onNetworkChange', (state) => {
199
+ if (state.connected) {
200
+ resolve(true);
201
+ // 移除监听
202
+ if (this.networkListener) {
203
+ this.networkListener.remove();
204
+ this.networkListener = null;
205
+ }
206
+ }
207
+ });
208
+ });
209
+ }
210
+ }
211
+
212
+ // 场景2:RCTEventEmitter 持有已销毁 View 引用
213
+ class CameraViewManager extends ViewManager {
214
+ private eventEmitter: RCTEventEmitter;
215
+ private viewMap: Map<number, Component> = new Map();
216
+
217
+ // 视图销毁后 eventEmitter 仍可能向该 view 发送事件
218
+ sendEventToView(viewTag: number, eventName: string, data: Object): void {
219
+ this.eventEmitter.emit(eventName, { viewTag, ...data });
220
+ // ⚠️ 如果 view 已被 onDropViewInstance 清理,事件仍会发送
221
+ }
222
+ }
223
+
224
+ // 正确实现:发送事件前检查 view 是否仍存在
225
+ class CameraViewManager extends ViewManager {
226
+ private eventEmitter: RCTEventEmitter;
227
+ private viewMap: Map<number, Component> = new Map();
228
+
229
+ sendEventToView(viewTag: number, eventName: string, data: Object): void {
230
+ if (!this.viewMap.has(viewTag)) {
231
+ return; // 视图已销毁,不再发送事件
232
+ }
233
+ this.eventEmitter.emit(eventName, { viewTag, ...data });
234
+ }
235
+
236
+ onDropViewInstance(view: Component): void {
237
+ this.viewMap.delete(view.getTag()); // 从 Map 中移除
238
+ super.onDropViewInstance(view);
239
+ }
240
+ }
241
+ ```
242
+
243
+ ---
244
+
245
+ ## 3. 鸿蒙 API 事件订阅管理
246
+
247
+ ### 3.1 @ohos 模块事件订阅
248
+
249
+ | 检查项 | 问题描述 | 风险等级 | 扣分 |
250
+ |--------|----------|----------|------|
251
+ | `on/off` 对称性 | 每个 `on()` 是否有对应 `off()` | **致命** | 无对应 -20 |
252
+ | 回调参数匹配 | `off()` 是否传入正确的回调函数引用 | | 参数错误 -12 |
253
+ | 多次注册风险 | 同一事件是否可能被多次注册监听 | 高 | 存在风险 -15 |
254
+ | TurboModule 级 vs View 级监听 | TurboModule 级监听不应在 View 销毁时取消 | 中 | 混淆 -10 |
255
+
256
+ **致命问题示例**:
257
+
258
+ ```typescript
259
+ // 致命:每次调用都注册新监听器,无对应取消
260
+ class BleTurboModule extends TurboModule {
261
+ async setNotifyValue(remoteId: string, characteristic: ble.BLECharacteristic): Promise<void> {
262
+ const gatt = this.mConnectedDevices.get(remoteId);
263
+ gatt.on('BLECharacteristicChange', (data) => {
264
+ this.onCharacteristicReceived(remoteId, data);
265
+ });
266
+ // ⚠️ 每次调用注册新监听器,断开连接时未取消!
267
+ }
268
+ }
269
+
270
+ // 正确实现:使用 Map 保存回调引用
271
+ class BleTurboModule extends TurboModule {
272
+ private characteristicCallbacks: Map<string, Function> = new Map();
273
+
274
+ async setNotifyValue(remoteId: string, characteristic: ble.BLECharacteristic): Promise<void> {
275
+ const gatt = this.mConnectedDevices.get(remoteId);
276
+
277
+ // 先取消旧的监听器
278
+ const oldCallback = this.characteristicCallbacks.get(remoteId);
279
+ if (oldCallback) {
280
+ gatt.off('BLECharacteristicChange', oldCallback);
281
+ }
282
+
283
+ // 注册新的监听器并保存引用
284
+ const newCallback = (data) => this.onCharacteristicReceived(remoteId, data);
285
+ gatt.on('BLECharacteristicChange', newCallback);
286
+ this.characteristicCallbacks.set(remoteId, newCallback);
287
+ }
288
+
289
+ destroy(): void {
290
+ // 释放所有特征变化监听
291
+ for (const [remoteId, callback] of this.characteristicCallbacks) {
292
+ const gatt = this.mConnectedDevices.get(remoteId);
293
+ if (gatt) {
294
+ gatt.off('BLECharacteristicChange', callback);
295
+ }
296
+ }
297
+ this.characteristicCallbacks.clear();
298
+ }
299
+ }
300
+ ```
301
+
302
+ ### 3.2 特殊系统资源释放
303
+
304
+ | 检查项 | 问题描述 | 风险等级 | 扣分 |
305
+ |--------|----------|----------|------|
306
+ | GattClientDevice.close | BLE Gatt 客户端是否调用 close() | **致命** | 未调用 -20 |
307
+ | 扫描状态清理 | `ble.stopBLEScan()` 是否在适当时机调用 | | 未调用 -15 |
308
+ | 地理位置监听 | `geoLocationManager.off()` 是否取消 | 高 | 未取消 -15 |
309
+ | Sensor 注销 | `sensor.off()` 是否取消 | **致命** | 未注销 -20 |
310
+ | 网络状态监听 | `connection.on/off` 是否对称 | 高 | 不对称 -12 |
311
+ | 数据库连接未关闭 | rdb 关闭未调用 close | **致命** | 未关闭 -20 |
312
+ | Timer 未清除 | setInterval/setTimeout 未在 destroy 时清除 | 高 | 未清除 -12 |
313
+
314
+ **致命泄漏场景**:
315
+
316
+ ```typescript
317
+ // 致命:GattClientDevice 创建后未释放
318
+ class BleTurboModule extends TurboModule {
319
+ async scanCallback(device: ble.ScanResult): void {
320
+ const gattClient = ble.createGattClientDevice(device.getMac());
321
+ const deviceName = await gattClient.getDeviceName();
322
+ // ⚠️ gattClient 未调用 close(),每次扫描都泄漏!
323
+ }
324
+ }
325
+
326
+ // 致命:服务查询创建的 GattClientDevice 未释放
327
+ async getServices(remoteId: string): Promise<ble.GattService[]> {
328
+ const gattClient = ble.createGattClientDevice(remoteId);
329
+ const services = await gattClient.getServices();
330
+ return services; // ⚠️ gattClient 未释放
331
+ }
332
+
333
+ // 正确实现:使用 try-finally 确保释放
334
+ async getServices(remoteId: string): Promise<ble.GattService[]> {
335
+ const gattClient = ble.createGattClientDevice(remoteId);
336
+ try {
337
+ const services = await gattClient.getServices();
338
+ return services;
339
+ } finally {
340
+ gattClient.close(); // 必须释放
341
+ }
342
+ }
343
+ ```
344
+
345
+ ---
346
+
347
+ ## 4. 闭包与上下文泄露
348
+
349
+ ### 4.1 闭包循环引用
350
+
351
+ | 检查项 | 问题描述 | 风险等级 | 扣分 |
352
+ |--------|----------|----------|------|
353
+ | this 捕获 | 箭头函数是否捕获 `this` 引用且未在 destroy 释放 | 高 | 捕获且未释放 -15 |
354
+ | 闭包保留 | 闭包是否保留外部变量引用且无法释放 | | 保留且无法释放 -12 |
355
+ | RN 回调链泄露 | JS → Native → JS 回调链中的闭包引用是否正确断开 | 高 | 未断开 -12 |
356
+ | 异步回调持有 stale context | setTimeout/Promise 回调中引用已销毁 TurboModule 上下文 | 中 | 引用 -10 |
357
+
358
+ **循环引用检测**:
359
+
360
+ ```typescript
361
+ // 风险:箭头函数捕获 this,形成循环引用
362
+ class BleTurboModule extends TurboModule {
363
+ private gatt: ble.GattClientDevice;
364
+
365
+ connect(): void {
366
+ this.gatt.on('BLEConnectionStateChange', (state) => {
367
+ this.onConnectionStateChanged(state); // this 被捕获
368
+ });
369
+ // 循环链:this.gatt → 回调 → this → this.gatt
370
+ }
371
+ }
372
+
373
+ // 正确实现:在构造函数中预创建回调,destroy 时清除引用
374
+ class BleTurboModule extends TurboModule {
375
+ private gatt: ble.GattClientDevice;
376
+ private connectionCallback: Function;
377
+
378
+ constructor() {
379
+ this.connectionCallback = (state: ble.ProfileConnectionState) => {
380
+ this.onConnectionStateChanged(state);
381
+ };
382
+ }
383
+
384
+ connect(): void {
385
+ this.gatt.on('BLEConnectionStateChange', this.connectionCallback);
386
+ }
387
+
388
+ destroy(): void {
389
+ if (this.gatt) {
390
+ this.gatt.off('BLEConnectionStateChange', this.connectionCallback);
391
+ }
392
+ this.connectionCallback = null; // 清除引用,打破循环
393
+ }
394
+ }
395
+ ```
396
+
397
+ ### 4.2 RN 特有回调链泄露
398
+
399
+ ```typescript
400
+ // 风险:TurboModule 级回调持有 View 实例引用
401
+ class CameraTurboModule extends TurboModule {
402
+ private onFrameCallback: ((frameData: Object) => void) | null = null;
403
+
404
+ startCamera(viewTag: number): void {
405
+ const view = this.viewManager.getViewByTag(viewTag);
406
+ this.onFrameCallback = (frameData: Object) => {
407
+ view.updateFrame(frameData); // 持有 view 引用
408
+ };
409
+ // ⚠️ view 销毁后回调仍持有引用
410
+ }
411
+ }
412
+
413
+ // 正确实现:使用 viewTag 而非 view 引用
414
+ class CameraTurboModule extends TurboModule {
415
+ private onFrameCallbacks: Map<number, Function> = new Map();
416
+
417
+ startCamera(viewTag: number): void {
418
+ this.onFrameCallbacks.set(viewTag, (frameData: Object) => {
419
+ const view = this.viewManager.getViewByTag(viewTag);
420
+ if (view) {
421
+ view.updateFrame(frameData); // 按需获取 view
422
+ }
423
+ });
424
+ }
425
+
426
+ // View 销毁时移除回调
427
+ removeFrameCallback(viewTag: number): void {
428
+ this.onFrameCallbacks.delete(viewTag);
429
+ }
430
+
431
+ destroy(): void {
432
+ this.onFrameCallbacks.clear();
433
+ }
434
+ }
435
+ ```
436
+
437
+ ### 4.3 Map/Set 数据清理
438
+
439
+ | 检查项 | 问题描述 | 风险等级 | 扣分 |
440
+ |--------|----------|----------|------|
441
+ | Map.clear() | TurboModule Map 数据是否在 destroy 时调用 clear() | | 未清理 -10 |
442
+ | View 实例 Map | ViewManager 维护的 View 实例 Map 是否在 onDropViewInstance 删除 | | 未删除 -12 |
443
+ | 静态 Map 风险 | 静态 Map 是否无限累积 | **致命** | 累积风险 -15 |
444
+ | 缓存上限 | 是否设置缓存大小上限 | 中 | 无上限 -8 |
445
+
446
+ **Map 清理检查**:
447
+
448
+ ```typescript
449
+ // 问题:ViewManager View 实例 Map 未在 onDropViewInstance 删除
450
+ class CameraViewManager extends ViewManager {
451
+ private viewInstances: Map<number, CameraView> = new Map();
452
+
453
+ createViewInstance(viewTag: number): CameraView {
454
+ const view = new CameraView();
455
+ this.viewInstances.set(viewTag, view);
456
+ return view;
457
+ }
458
+
459
+ onDropViewInstance(view: Component): void {
460
+ super.onDropViewInstance(view);
461
+ // ⚠️ viewInstances 中该 viewTag 对应的条目未删除!
462
+ }
463
+ }
464
+
465
+ // 正确实现
466
+ class CameraViewManager extends ViewManager {
467
+ private viewInstances: Map<number, CameraView> = new Map();
468
+
469
+ createViewInstance(viewTag: number): CameraView {
470
+ const view = new CameraView();
471
+ this.viewInstances.set(viewTag, view);
472
+ return view;
473
+ }
474
+
475
+ onDropViewInstance(view: Component): void {
476
+ const viewTag = view.getTag();
477
+ const cameraView = this.viewInstances.get(viewTag);
478
+ if (cameraView) {
479
+ cameraView.release(); // 释放视图资源
480
+ this.viewInstances.delete(viewTag); // 从 Map 中移除
481
+ }
482
+ super.onDropViewInstance(view);
483
+ }
484
+ }
485
+ ```
486
+
487
+ ---
488
+
489
+ ## 5. NAPI 引用管理
490
+
491
+ ### 5.1 NAPI 引用生命周期
492
+
493
+ | 检查项 | 问题描述 | 风险等级 | 扣分 |
494
+ |--------|----------|----------|------|
495
+ | `napi_ref` 释放 | 是否在 dispose/destroy 时调用 `napi_delete_reference` | **致命** | 未释放 -20 |
496
+ | Handle Scope 清理 | 是否正确关闭 `napi_handle_scope` | **致命** | 未关闭 -20 |
497
+ | finalize 回调 | `napi_wrap` 是否提供 finalize 回调释放 Native 对象 | **致命** | 缺失 -20 |
498
+ | 引用计数平衡 | `napi_create_reference` `napi_delete_reference` 数量是否匹配 | 高 | 不平衡 -18 |
499
+ | ArrayBuffer 外部资源泄露 | `napi_create_external` ArrayBuffer 的 finalize 回调缺失 | 高 | 缺失 -15 |
500
+ | NAPI 线程安全函数引用泄露 | `napi_create_threadsafe_function` 后未调用 `napi_release_threadsafe_function` | 高 | 未释放 -15 |
501
+
502
+ **C++ NAPI 管理示例**:
503
+
504
+ ```cpp
505
+ // ❌ 致命:NAPI 引用未释放,且缺少 finalize 回调
506
+ class NativeTurboModule {
507
+ private:
508
+ napi_ref callbackRef;
509
+
510
+ public:
511
+ void registerCallback(napi_env env, napi_value callback) {
512
+ napi_create_reference(env, callback, 1, &this->callbackRef);
513
+ // 缺少释放逻辑!
514
+ // 缺少 finalize 回调!
515
+ }
516
+ };
517
+
518
+ // ✅ 正确实现
519
+ class NativeTurboModule {
520
+ private:
521
+ napi_ref callbackRef = nullptr;
522
+
523
+ public:
524
+ void registerCallback(napi_env env, napi_value callback) {
525
+ if (this->callbackRef) {
526
+ napi_delete_reference(env, this->callbackRef);
527
+ }
528
+ napi_create_reference(env, callback, 1, &this->callbackRef);
529
+ }
530
+
531
+ void destroy(napi_env env) {
532
+ if (this->callbackRef) {
533
+ napi_delete_reference(env, this->callbackRef);
534
+ this->callbackRef = nullptr;
535
+ }
536
+ }
537
+ };
538
+
539
+ // 正确实现:napi_wrap 带 finalize 回调
540
+ void NativeObjectFinalize(napi_env env, void* nativeObj, void* hint) {
541
+ auto* obj = static_cast<NativeResource*>(nativeObj);
542
+ delete obj; // 在 finalize 中释放 Native 对象
543
+ }
544
+
545
+ napi_status WrapNativeObject(napi_env env, napi_value jsThis, NativeResource* nativeObj) {
546
+ napi_ref ref;
547
+ return napi_wrap(env, jsThis, nativeObj, NativeObjectFinalize, nullptr, &ref);
548
+ }
549
+ ```
550
+
551
+ ### 5.2 Handle Scope 管理
552
+
553
+ ```cpp
554
+ // ❌ 致命:Handle Scope 未关闭
555
+ napi_value ProcessData(napi_env env, napi_callback_info info) {
556
+ napi_handle_scope scope;
557
+ napi_open_handle_scope(env, &scope);
558
+
559
+ napi_value result;
560
+ napi_create_string_utf8(env, "hello", NAPI_AUTO_LENGTH, &result);
561
+
562
+ // ⚠️ 缺少 napi_close_handle_scope!
563
+ return result;
564
+ }
565
+
566
+ // ✅ 正确实现:使用 RAII 或手动关闭
567
+ napi_value ProcessData(napi_env env, napi_callback_info info) {
568
+ napi_handle_scope scope;
569
+ napi_open_handle_scope(env, &scope);
570
+
571
+ napi_value result;
572
+ napi_create_string_utf8(env, "hello", NAPI_AUTO_LENGTH, &result);
573
+
574
+ napi_close_handle_scope(env, scope); // 必须关闭
575
+ return result;
576
+ }
577
+ ```
578
+
579
+ ---
580
+
581
+ ## 6. TurboModule/JSI 绑定内存
582
+
583
+ ### 6.1 TurboModule C++ 生命周期管理
584
+
585
+ | 检查项 | 问题描述 | 风险等级 | 扣分 |
586
+ |--------|----------|----------|------|
587
+ | TurboModule C++ 实例释放 | C++ 实例在 JS 对象回收后是否释放 | **致命** | 未释放 -15 |
588
+ | JSI HostFunction 泄露 | HostFunction 绑定在 Runtime 销毁时是否释放 | **致命** | 未释放 -15 |
589
+ | JSI HostObject 属性回调泄露 | HostObject getter/setter 闭包持有外部引用未清理 | | 未清理 -12 |
590
+ | 跨语言回调链泄露 | C++ 层回调引用 JS 函数的双向引用是否正确断开 | | 未断开 -12 |
591
+ | Codegen 生成代码资源释放 | Codegen 自动生成的代码中资源释放逻辑是否完整 | 中 | 不完整 -10 |
592
+ | TurboModule C++ 与 JS 对象生命周期不一致 | C++ 侧实例比 JS 侧对象存活更久,导致悬垂引用 | **致命** | 不一致 -15 |
593
+
594
+ **TurboModule C++ 资源释放示例**:
595
+
596
+ ```cpp
597
+ // ❌ 致命:TurboModule C++ 实例未在析构时释放资源
598
+ class BleTurboModule : public TurboModule {
599
+ private:
600
+ std::map<std::string, GattClient*> mConnectedDevices;
601
+ napi_ref mJsCallbackRef;
602
+
603
+ public:
604
+ ~BleTurboModule() {
605
+ // ⚠️ 未释放 mConnectedDevices 中的 GattClient
606
+ // ⚠️ 未释放 mJsCallbackRef
607
+ }
608
+ };
609
+
610
+ // ✅ 正确实现
611
+ class BleTurboModule : public TurboModule {
612
+ private:
613
+ std::map<std::string, GattClient*> mConnectedDevices;
614
+ napi_ref mJsCallbackRef = nullptr;
615
+
616
+ public:
617
+ ~BleTurboModule() {
618
+ // 1. 释放所有 GattClient
619
+ for (auto& [id, client] : mConnectedDevices) {
620
+ delete client;
621
+ }
622
+ mConnectedDevices.clear();
623
+
624
+ // 2. 释放 NAPI 引用(注意:需要 env,通常在 destroy 中处理)
625
+ }
626
+
627
+ void destroy(napi_env env) {
628
+ // 释放 NAPI 引用
629
+ if (mJsCallbackRef) {
630
+ napi_delete_reference(env, mJsCallbackRef);
631
+ mJsCallbackRef = nullptr;
632
+ }
633
+ }
634
+ };
635
+ ```
636
+
637
+ ### 6.2 JSI HostFunction/HostObject 管理
638
+
639
+ ```cpp
640
+ // ❌ 风险:HostFunction 闭包持有外部引用
641
+ class MyHostObject : public jsi::HostObject {
642
+ private:
643
+ jsi::Function mCallback; // 持有 JS 函数引用
644
+
645
+ public:
646
+ jsi::Value get(jsi::Runtime& rt, const jsi::PropNameID& name) override {
647
+ if (name.utf8(rt) == "callback") {
648
+ return mCallback; // 返回 JS 函数
649
+ }
650
+ // ⚠️ Runtime 销毁时 mCallback 未释放
651
+ }
652
+ };
653
+
654
+ // ✅ 正确实现:在 Runtime 销毁时清理
655
+ class MyHostObject : public jsi::HostObject {
656
+ private:
657
+ std::optional<jsi::Function> mCallback;
658
+
659
+ public:
660
+ jsi::Value get(jsi::Runtime& rt, const jsi::PropNameID& name) override {
661
+ if (name.utf8(rt) == "callback" && mCallback.has_value()) {
662
+ return mCallback.value();
663
+ }
664
+ return jsi::Value::undefined();
665
+ }
666
+
667
+ // Runtime 销毁时由 TurboModule 的 destroy 调用
668
+ void invalidate() {
669
+ mCallback.reset(); // 清除 JS 函数引用
670
+ }
671
+ };
672
+ ```
673
+
674
+ ---
675
+
676
+ ## 7. C/C++ 层内存泄露
677
+
678
+ ### 7.1 Native 内存分配释放
679
+
680
+ | 检查项 | 问题描述 | 风险等级 | 扣分 |
681
+ |--------|----------|----------|------|
682
+ | malloc/free 匹配 | 每个 `malloc` 是否有对应 `free` | **致命** | 不匹配 -15 |
683
+ | new/delete 匹配 | 每个 `new` 是否有对应 `delete` | **致命** | 不匹配 -15 |
684
+ | new[]/delete[] 匹配 | `new[]` 是否使用 `delete[]` 释放 | **致命** | 误用 -15 |
685
+ | 智能指针循环引用 | `shared_ptr` 循环引用导致引用计数永不为零 | **致命** | 循环引用 -15 |
686
+ | 异常路径释放 | 异常发生时是否释放已分配内存 | 高 | 未释放 -12 |
687
+ | RAII 模式 | 是否优先使用智能指针 | 中 | 未使用 -10 |
688
+ | 全局/静态缓冲区无限增长 | 全局 vector/map 无上限增长 | 高 | 无清理 -12 |
689
+
690
+ **RAII 推荐模式**:
691
+
692
+ ```cpp
693
+ // ✅ 使用智能指针自动管理
694
+ #include <memory>
695
+
696
+ void processData() {
697
+ std::unique_ptr<Buffer> buffer = std::make_unique<Buffer>(1024);
698
+ // 自动释放,无需手动 delete
699
+
700
+ // 或使用 std::shared_ptr 处理共享所有权
701
+ std::shared_ptr<Resource> resource = std::make_shared<Resource>();
702
+ }
703
+
704
+ // ✅ RAII wrapper 模式
705
+ class BufferGuard {
706
+ private:
707
+ void* buf;
708
+ public:
709
+ BufferGuard(size_t size) : buf(malloc(size)) {}
710
+ ~BufferGuard() {
711
+ if (buf) free(buf); // 自动释放
712
+ }
713
+ // 禁止拷贝
714
+ BufferGuard(const BufferGuard&) = delete;
715
+ BufferGuard& operator=(const BufferGuard&) = delete;
716
+ };
717
+ ```
718
+
719
+ **智能指针循环引用检测**:
720
+
721
+ ```cpp
722
+ // 致命:shared_ptr 循环引用
723
+ class TurboModuleImpl;
724
+ class CallbackHolder : public std::enable_shared_from_this<CallbackHolder> {
725
+ public:
726
+ std::shared_ptr<TurboModuleImpl> module; // 持有 module 引用
727
+ };
728
+
729
+ class TurboModuleImpl : public std::enable_shared_from_this<TurboModuleImpl> {
730
+ public:
731
+ std::shared_ptr<CallbackHolder> callback; // 持有 callback 引用
732
+ // 循环链:module ↔ callback,引用计数永不为零!
733
+ };
734
+
735
+ // ✅ 正确实现:使用 weak_ptr 打破循环
736
+ class CallbackHolder {
737
+ public:
738
+ std::weak_ptr<TurboModuleImpl> module; // 弱引用,不增加引用计数
739
+ };
740
+
741
+ class TurboModuleImpl : public std::enable_shared_from_this<TurboModuleImpl> {
742
+ public:
743
+ std::shared_ptr<CallbackHolder> callback;
744
+ };
745
+ ```
746
+
747
+ ---
748
+
749
+ ## 8. RN 鸿蒙化特有内存风险
750
+
751
+ ### 8.1 跨语言生命周期不一致
752
+
753
+ | 检查项 | 问题描述 | 风险等级 | 扣分 |
754
+ |--------|----------|----------|------|
755
+ | ArkTS on/C++ resource 不对称释放 | ArkTS 层调用 off() C++ 层未释放对应资源,或反过来 | **致命** | 不对称 -15 |
756
+ | Native 对象生命周期不一致 | C++ Native 对象与 ArkTS 包装对象生命周期不一致 | **致命** | 不一致 -15 |
757
+ | TurboModule 与 View 生命周期混淆 | TurboModule 级资源不应在 View 销毁时释放,反之亦然 | 高 | 混淆 -12 |
758
+ | RN Bridge 消息队列累积 | 原生模块与 JS 之间的消息队列在模块销毁时未清空 | 中 | 未清空 -10 |
759
+
760
+ **跨语言释放不对称示例**:
761
+
762
+ ```typescript
763
+ // ❌ 致命:ArkTS 层 off() C++ 层未释放资源
764
+ class BleTurboModule extends TurboModule {
765
+ disconnect(remoteId: string): void {
766
+ const gatt = this.mConnectedDevices.get(remoteId);
767
+ gatt.off('BLEConnectionStateChange', this.connectionCallback);
768
+ this.mConnectedDevices.delete(remoteId);
769
+ // ⚠️ ArkTS 层取消了监听并从 Map 删除,但 C++ 层的 GattClient 未释放
770
+ }
771
+ }
772
+
773
+ // 正确实现:确保 C++ 层也释放对应资源
774
+ disconnect(remoteId: string): void {
775
+ const gatt = this.mConnectedDevices.get(remoteId);
776
+ if (gatt) {
777
+ gatt.off('BLEConnectionStateChange', this.connectionCallback);
778
+ gatt.close(); // 确保 C++ 层资源也释放
779
+ }
780
+ this.mConnectedDevices.delete(remoteId);
781
+ }
782
+ ```
783
+
784
+ ### 8.2 Spec Codegen 生成代码
785
+
786
+ ```typescript
787
+ // 风险:Codegen 生成的 TurboModule 代码中未实现资源释放
788
+ // Codegen 生成的接口:
789
+ interface NativeBleModule extends TurboModule {
790
+ startScan(): Promise<void>;
791
+ stopScan(): Promise<void>;
792
+ // ⚠️ 没有 destroy/invalidate 方法来释放资源
793
+ }
794
+
795
+ // 正确做法:在 Codegen 生成的接口基础上,补充资源释放逻辑
796
+ class BleTurboModuleImpl extends TurboModule implements NativeBleModuleSpec {
797
+ // 实现 Codegen 接口方法
798
+ startScan(): Promise<void> { ... }
799
+ stopScan(): Promise<void> { ... }
800
+
801
+ // 补充资源释放方法(在 TurboModule destroy 时调用)
802
+ destroy(): void {
803
+ // 释放 Codegen 未管理的资源
804
+ this.cleanupResources();
805
+ }
806
+ }
807
+ ```
808
+
809
+ ---
810
+
811
+ ## 9. 评分规则汇总
812
+
813
+ ### 致命问题(-20 分,最多扣 60 分)
814
+
815
+ | 问题类型 | 扣分 | 直接 0 分条件 |
816
+ |----------|------|---------------|
817
+ | TurboModule destroy 未释放资源 | -20 | 3 个致命问题 |
818
+ | onDropViewInstance 未清理 | -15 | 2 个致命 + ≥ 3 个严重 |
819
+ | NAPI 引用未释放 | -20 | 关键路径存在致命 |
820
+ | finalize 回调缺失 | -20 | 累计扣分 > 60 |
821
+ | Handle Scope 未关闭 | -20 | |
822
+ | malloc/free 不匹配 | -15 | |
823
+ | new/delete 不匹配 | -15 | |
824
+ | 鸿蒙 API 事件订阅无对应取消 | -20 | |
825
+ | NativeEventEmitter 监听未移除 | -15 | |
826
+ | GattClientDevice 未释放 | -20 | |
827
+ | Sensor 未注销 | -20 | |
828
+ | 数据库连接未关闭 | -20 | |
829
+
830
+ ### 严重问题(-12 分)
831
+
832
+ | 问题类型 | 扣分 |
833
+ |----------|------|
834
+ | Bridge 事件监听未移除 | -15 |
835
+ | 闭包循环引用风险 | -15 |
836
+ | Map 数据未清理 | -10 |
837
+ | View 实例 Map 未删除 | -12 |
838
+ | 跨语言回调链泄露 | -12 |
839
+ | JSI HostObject 属性回调泄露 | -12 |
840
+ | 扫描状态未清理 | -15 |
841
+ | Timer 未清除 | -12 |
842
+
843
+ ### 中等问题(-8 分)
844
+
845
+ | 问题类型 | 扣分 |
846
+ |----------|------|
847
+ | 回调参数匹配错误 | -12 |
848
+ | 释放顺序不当 | -8 |
849
+ | 初始化不完整 | -5 |
850
+ | TurboModule vs View 级监听混淆 | -10 |
851
+ | Codegen 生成代码资源释放不完整 | -10 |
852
+ | RN Bridge 消息队列未清空 | -10 |
853
+
854
+ ### 轻微问题(-5 分)
855
+
856
+ | 问题类型 | 扣分 |
857
+ |----------|------|
858
+ | 订阅状态未追踪 | -8 |
859
+ | 缓存无上限 | -8 |
860
+ | RAII 未使用 | -10 |
861
+ | 事件发送器未随 TurboModule 销毁 | -10 |
862
+
863
+ ### 观察项(0 分,不计入扣分)
864
+
865
+ 内存检视中发现的对内存**零影响**的问题,记入泄露风险清单但标注"观察(0 分)",注明不扣分理由及建议归属阶段(编码质量/一致性检视):
866
+
867
+ | 问题类型 | 处理 | 说明 |
868
+ |----------|------|------|
869
+ | 未接线的手写 TurboModule 死代码 | 观察(0 分) | Package 工厂未实例化,运行期零分配零调用(接线验证方法见 SKILL.md 1.3) |
870
+ | codegen 产物滞后(手写与 codegen 方法数不一致) | 观察(0 分) | 无内存影响,属一致性/维护性问题 |
871
+ | 死代码模块级单例(全仓库无 import) | 观察(0 分)或轻微(0~-5) | 运行期零分配,非严格泄露;被 import 的 eager 单例另按"加载即常驻"标定轻微~中等 |
872
+ | 无状态模块未重写 `__onDestroy__` | 豁免,不扣分 | 零监听/零句柄/零容器/零回调引用时属正确最小化设计(见第 1 节豁免规则) |
873
+
874
+ ---
875
+
876
+ ## 10. 零泄露面穷尽验证模式集(无状态库确认)
877
+
878
+ **用途**:疑似"零状态"库(纯 TurboModule、无 NativeComponent、业务全部映射到系统静态一次性 API)在下"零泄露面"结论前,必须用以下 rg 模式集穷尽验证,**全库 0 命中方可确认**。确认零泄露面后,`__onDestroy__` 缺失适用第 1 节的无状态模块豁免规则。
879
+
880
+ ```bash
881
+ # 1. 生命周期/监听/订阅
882
+ rg -n "__onDestroy__|onDestroy|aboutToDisappear|onDropViewInstance|addListener|removeListener|EventEmitter|addEventListener" <ets/cpp/js 目录>
883
+ # 2. 鸿蒙 on/off 订阅对
884
+ rg -n "\.on\(|\.off\(" <ets 目录>
885
+ # 3. 容器与静态状态
886
+ rg -n "Map<|Set<|static |private " <ets 目录> --glob '*.ts' --glob '*.ets'
887
+ # 4. C++ 内存与 NAPI(仅手写文件)
888
+ rg -n "new |malloc|napi_|delete |free\(" <cpp 目录> -g '<手写文件 pattern>'
889
+ # 5. 定时器
890
+ rg -n "setInterval|setTimeout" <ets 目录>
891
+ ```
892
+
893
+ **要点**:
894
+ - C++ 检查必须区分手写文件与 `generated/` 目录(用 `-g` 限定只查手写文件,如 `-g 'Cookies*'`);codegen 生成的空壳文件(EventEmitters/Props/ShadowNodes 等)单独确认"空实现无资源"即可,不逐文件深查
895
+ - 扫描范围须排除 `.upstream-cache`、`node_modules`、`oh_modules` 等缓存/依赖目录(与 Phase 0 扫描脚本排除规则一致)
896
+ - 全部 0 命中 → 报告注明"零泄露面,经六组模式穷尽验证";任一命中 → 按对应 Phase 检查项深入分析
897
+
898
+ ### 10.1 codegen 组件空壳快速判定
899
+
900
+ 无 NativeComponent 的库,codegen 仍会生成组件相关文件(EventEmitters/Props/ShadowNodes/ComponentDescriptor 等)。快速判定整组文件是否为空壳:
901
+
902
+ - 查看 Package 头文件(如 `BaseReactNativeXXXPackage.h`)中 `createComponentDescriptorProviders()` / `createComponentJSIBinderByName()` 的返回值是否为**空集合**
903
+ - 查看 `supportedComponentNames` / `supportedEventNames` 是否为**空 vector**
904
+
905
+ 两者均为空 → 整组组件文件按空壳处理,NativeComponent 维度记"**不适用**"而非"通过"(报告中明确标注,避免误读为已检查通过)。