@buaa_smat/hometrans 0.1.0

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.
@@ -0,0 +1,688 @@
1
+ import { createRequire as _htCreateRequire } from 'module'; const require = _htCreateRequire(import.meta.url);
2
+
3
+ // src/context/index.ts
4
+ import fs3 from "node:fs";
5
+ import path3 from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ import {
8
+ CallGraph as CallGraph2,
9
+ CallGraphBuilder,
10
+ Scene as Scene2,
11
+ SceneConfig
12
+ } from "arkanalyzer";
13
+
14
+ // src/context/analysis/ArkTsGitInfoAnalysis.ts
15
+ import {
16
+ AbstractInvokeExpr,
17
+ ArkAssignStmt,
18
+ CallGraphNode,
19
+ ArkInvokeStmt,
20
+ fetchDependenciesFromFile,
21
+ FunctionType,
22
+ parseJsonText
23
+ } from "arkanalyzer";
24
+ import path2 from "path";
25
+ import fs2 from "fs";
26
+
27
+ // src/context/utils/util.ts
28
+ import LoggerMod, { LOG_MODULE_TYPE } from "arkanalyzer/lib/utils/logger.js";
29
+ import path from "path";
30
+ import fs from "fs";
31
+ var Logger = LoggerMod.default ?? LoggerMod;
32
+ var logger = Logger.getLogger(LOG_MODULE_TYPE.DEFAULT, "util");
33
+ function safePush(target, source, maxBatchSize = 1e4) {
34
+ for (let i = 0; i < source.length; i += maxBatchSize) {
35
+ const batch = source.slice(i, i + maxBatchSize);
36
+ target.push(...batch);
37
+ }
38
+ }
39
+ function getAllFiles(dirPath, exts, filenameArr = [], visited = /* @__PURE__ */ new Set()) {
40
+ if (!fs.existsSync(dirPath)) {
41
+ logger.error(`'${dirPath}' is not exist, please check!`);
42
+ return filenameArr;
43
+ }
44
+ const realSrc = fs.realpathSync(dirPath);
45
+ if (visited.has(realSrc)) {
46
+ return filenameArr;
47
+ }
48
+ visited.add(realSrc);
49
+ fs.readdirSync(realSrc).forEach((fileName) => {
50
+ if (shouldSkipFile(fileName)) {
51
+ return;
52
+ }
53
+ const realFile = path.resolve(realSrc, fileName);
54
+ if (fs.statSync(realFile).isDirectory()) {
55
+ getAllFiles(realFile, exts, filenameArr, visited);
56
+ } else {
57
+ if (exts.length === 0) {
58
+ filenameArr.push(realFile);
59
+ } else if (shouldAddFile(realFile, exts)) {
60
+ filenameArr.push(realFile);
61
+ }
62
+ }
63
+ });
64
+ return filenameArr;
65
+ }
66
+ function shouldSkipFile(fileName) {
67
+ return ["oh_modules", "node_modules", "hvigorfile.ts", "ohosTest"].includes(fileName);
68
+ }
69
+ function shouldAddFile(filePath, exts) {
70
+ if (exts.length === 0) {
71
+ return true;
72
+ }
73
+ const ext = path.extname(filePath).toLowerCase();
74
+ return exts.includes(ext);
75
+ }
76
+ function getFilePath(parts) {
77
+ return path.join(...parts);
78
+ }
79
+
80
+ // src/context/analysis/ArkTsGitInfoAnalysis.ts
81
+ var ModuleDependency = class {
82
+ moduleName;
83
+ modulePath;
84
+ pluralJsons;
85
+ stringJsons;
86
+ strArrayJsons;
87
+ };
88
+ var Resource = class {
89
+ resource = /* @__PURE__ */ new Map();
90
+ };
91
+ var GitItem = class {
92
+ mode;
93
+ path;
94
+ lineInterval;
95
+ contentAfter;
96
+ contentBefore;
97
+ };
98
+ var ArkTsGitInfoAnalysis = class {
99
+ scene;
100
+ mode;
101
+ callGraph;
102
+ originLines;
103
+ copiedLines;
104
+ dependencyFileDict = /* @__PURE__ */ new Map();
105
+ dependencyResourceMap = /* @__PURE__ */ new Map();
106
+ gitItem;
107
+ moduleDependencies;
108
+ modulePathDependenciesMap = /* @__PURE__ */ new Map();
109
+ maxCallDepth = 4;
110
+ analysis = (arkFile, gitItem, callGraph, mode) => {
111
+ this.callGraph = callGraph;
112
+ this.mode = mode;
113
+ this.originLines = arkFile.getCode().split("\n");
114
+ let copiedLinesInDict = this.dependencyFileDict.get(arkFile);
115
+ if (!copiedLinesInDict) {
116
+ this.copiedLines = new Array(this.originLines.length);
117
+ this.dependencyFileDict.set(arkFile, this.copiedLines);
118
+ } else {
119
+ this.copiedLines = copiedLinesInDict;
120
+ }
121
+ this.gitItem = gitItem;
122
+ this.scene = arkFile.getScene();
123
+ const modulePath = arkFile.getModuleScene().getModulePath();
124
+ if (this.modulePathDependenciesMap.has(modulePath)) {
125
+ this.moduleDependencies = this.modulePathDependenciesMap.get(modulePath);
126
+ } else {
127
+ this.moduleDependencies = /* @__PURE__ */ new Map();
128
+ this.getModuleDependency(arkFile.getModuleScene().getModulePath());
129
+ this.modulePathDependenciesMap.set(modulePath, this.moduleDependencies);
130
+ }
131
+ this.analysisFileStructureTree(arkFile);
132
+ for (let i = gitItem.lineInterval[0]; i <= gitItem.lineInterval[1]; i++) {
133
+ if (this.copiedLines[i - 1] === void 0) {
134
+ this.copiedLines[i - 1] = this.originLines[i - 1];
135
+ }
136
+ }
137
+ return this.dependencyFileDict;
138
+ };
139
+ getModuleDependency(modulePath) {
140
+ let OhPkgFilePath = path2.join(modulePath, "oh-package.json5");
141
+ console.log(`modulePath ${modulePath} OhPkgFilePath ${OhPkgFilePath}`);
142
+ let libOhPkgContent = fetchDependenciesFromFile(OhPkgFilePath);
143
+ let dependencies = libOhPkgContent.dependencies;
144
+ console.dir(dependencies, { depth: null });
145
+ if (!dependencies) {
146
+ return;
147
+ }
148
+ Object.entries(dependencies).forEach(([key, value]) => {
149
+ if (key.startsWith("@ohos") && value.startsWith("file:")) {
150
+ const targetModulePath = path2.resolve(modulePath, value.replace("file:", ""));
151
+ if (!this.moduleDependencies.has(targetModulePath)) {
152
+ const targetModuleResourcePath = path2.join(targetModulePath, "src", "main", "resources");
153
+ const moduleDependency = new ModuleDependency();
154
+ moduleDependency.moduleName = key.replace("@ohos/", "");
155
+ moduleDependency.modulePath = targetModulePath;
156
+ moduleDependency.pluralJsons = this.loadJsonResources(targetModuleResourcePath, "plural.json");
157
+ moduleDependency.strArrayJsons = this.loadJsonResources(targetModuleResourcePath, "strarray.json");
158
+ moduleDependency.stringJsons = this.loadJsonResources(targetModuleResourcePath, "string.json");
159
+ this.moduleDependencies.set(moduleDependency.modulePath, moduleDependency);
160
+ this.getModuleDependency(targetModulePath);
161
+ }
162
+ }
163
+ });
164
+ }
165
+ resolveAnonymousMethod(arkMethod, arkClass) {
166
+ let callStmtStartLine = arkMethod.getLine();
167
+ let callStmtEndLine = this.getEndLine(callStmtStartLine, arkMethod.getCode());
168
+ let outerArkMethod = arkMethod.getOuterMethod();
169
+ while (outerArkMethod && outerArkMethod.getName().startsWith("%AM")) {
170
+ arkMethod = outerArkMethod;
171
+ outerArkMethod = arkMethod.getOuterMethod();
172
+ }
173
+ if (outerArkMethod) {
174
+ if (outerArkMethod.getName() === "%instInit") {
175
+ let targetArkField;
176
+ for (let arkField of arkClass.getFields()) {
177
+ const stmts = arkField.getInitializer() ?? [];
178
+ for (let stmt of stmts) {
179
+ if (stmt instanceof ArkAssignStmt) {
180
+ const rightOp = stmt.getRightOp();
181
+ let type = rightOp.getType();
182
+ if (type instanceof FunctionType && type.getMethodSignature() === arkMethod.getSignature()) {
183
+ arkMethod = this.scene.getMethod(type.getMethodSignature());
184
+ targetArkField = arkField;
185
+ callStmtStartLine = targetArkField.getOriginPosition().getLineNo();
186
+ callStmtEndLine = this.getEndLine(callStmtStartLine, targetArkField.getCode());
187
+ break;
188
+ }
189
+ }
190
+ }
191
+ if (targetArkField) {
192
+ break;
193
+ }
194
+ }
195
+ } else if (outerArkMethod.getName() === "%dflt") {
196
+ let locals = outerArkMethod.getBody().getLocals();
197
+ locals.forEach((local, str) => {
198
+ let stmt = local.getDeclaringStmt();
199
+ if (stmt instanceof ArkAssignStmt) {
200
+ const rightOp = stmt.getRightOp();
201
+ let type = rightOp.getType();
202
+ if (type instanceof FunctionType && type.getMethodSignature() === arkMethod.getSignature()) {
203
+ arkMethod = this.scene.getMethod(type.getMethodSignature());
204
+ callStmtStartLine = stmt.getOriginPositionInfo().getLineNo();
205
+ callStmtEndLine = this.getEndLine(callStmtStartLine, stmt.getOriginalText());
206
+ }
207
+ }
208
+ });
209
+ } else {
210
+ callStmtStartLine = outerArkMethod.getLine();
211
+ callStmtEndLine = this.getEndLine(callStmtStartLine, outerArkMethod.getCode());
212
+ }
213
+ }
214
+ return { arkMethod, startLine: callStmtStartLine, endLine: callStmtEndLine };
215
+ }
216
+ copyClassHeader(arkClass, copiedLines, originLines, includeMinLine) {
217
+ let classStartLine = arkClass.getLine();
218
+ let classEndLine = this.getEndLine(classStartLine, arkClass.getCode());
219
+ let minLineNumber = classEndLine;
220
+ if (arkClass.getFields().length !== 0) {
221
+ let fields = arkClass.getFields();
222
+ fields.sort((a, b) => a.getOriginPosition().getLineNo() - b.getOriginPosition().getLineNo());
223
+ let firstFieldLine = fields[0].getOriginPosition().getLineNo();
224
+ minLineNumber = Math.min(firstFieldLine, minLineNumber);
225
+ }
226
+ if (arkClass.getMethods().length !== 0) {
227
+ let methods = arkClass.getMethods();
228
+ methods = methods.filter((method) => this.isMethodValid(method)).sort((a, b) => a.getLine() - b.getLine());
229
+ if (methods.length !== 0) {
230
+ let firstMethodLine = methods[0].getLine();
231
+ if (firstMethodLine !== void 0 && firstMethodLine !== null) {
232
+ minLineNumber = Math.min(firstMethodLine, minLineNumber);
233
+ }
234
+ }
235
+ }
236
+ const end = includeMinLine ? minLineNumber : minLineNumber - 1;
237
+ for (let i = classStartLine; i <= end; i++) {
238
+ copiedLines[i - 1] = originLines[i - 1];
239
+ }
240
+ copiedLines[classEndLine - 1] = originLines[classEndLine - 1];
241
+ }
242
+ loadJsonResources(resourcePath, fileName) {
243
+ const jsonItems = [];
244
+ const results = getAllFiles(resourcePath, [fileName]);
245
+ for (const result of results) {
246
+ const configText = fs2.readFileSync(result, "utf8");
247
+ const json = parseJsonText(configText);
248
+ jsonItems.push({ path: result, content: json });
249
+ }
250
+ return jsonItems;
251
+ }
252
+ getEndLine(startLine, code) {
253
+ return startLine + (code ? code.split("\n").length - 1 : 0);
254
+ }
255
+ isMethodValid(method) {
256
+ return !(method.getName() === "constructor" && method.getCode() === "") && method.getLine() !== -1 && method.getCode() !== void 0;
257
+ }
258
+ analysisFileStructureTree(arkFile) {
259
+ arkFile.getModuleScene().getModulePath();
260
+ let arkClasses = arkFile.getClasses().filter((cls) => cls.getName() === "%dflt" || !cls.getName().startsWith("%"));
261
+ let classNames = arkClasses.map((cls) => cls.getName());
262
+ for (const arkClass of arkClasses) {
263
+ if (arkClass.getName() === "%dflt") {
264
+ this.analysisClassStructureTree(arkClass);
265
+ } else {
266
+ let classStartLine = arkClass.getLine();
267
+ if (classStartLine === -1) {
268
+ this.analysisClassStructureTree(arkClass);
269
+ } else {
270
+ let classEndLine = this.getEndLine(classStartLine, arkClass.getCode());
271
+ let classLineInterval = [classStartLine, classEndLine];
272
+ if (!(classLineInterval[0] > this.gitItem.lineInterval[1] || classLineInterval[1] < this.gitItem.lineInterval[0])) {
273
+ this.copyClassHeader(arkClass, this.copiedLines, this.originLines, false);
274
+ this.analysisClassStructureTree(arkClass);
275
+ }
276
+ }
277
+ }
278
+ }
279
+ }
280
+ getInvokeExprFromStmt(stmt) {
281
+ if (stmt instanceof ArkInvokeStmt) {
282
+ return stmt.getInvokeExpr();
283
+ } else if (stmt instanceof ArkAssignStmt) {
284
+ const rightOp = stmt.getRightOp();
285
+ if (rightOp instanceof AbstractInvokeExpr) {
286
+ return rightOp;
287
+ }
288
+ }
289
+ }
290
+ analysisMethodResource(method) {
291
+ const stmts = method.getBody()?.getCfg().getStmts();
292
+ if (!stmts) {
293
+ return;
294
+ }
295
+ for (let stmt of stmts) {
296
+ const invokeExpr = this.getInvokeExprFromStmt(stmt);
297
+ if (!invokeExpr) {
298
+ continue;
299
+ }
300
+ const invokeSignature = invokeExpr.getMethodSignature();
301
+ let clazz = method.getDeclaringArkClass();
302
+ let methodName = invokeSignature.getMethodSubSignature().getMethodName();
303
+ let moduleName = clazz.getDeclaringArkFile().getModuleName();
304
+ if (moduleName === void 0) {
305
+ continue;
306
+ }
307
+ if (methodName === "$r") {
308
+ const resourceLabel = invokeExpr.getArgs()[0].toString().slice(1, -1);
309
+ const array = resourceLabel.split(".");
310
+ if (array.length === 3) {
311
+ const scope = array[0];
312
+ if (scope !== "app") {
313
+ continue;
314
+ }
315
+ const category = array[1];
316
+ const name = array[2];
317
+ for (const moduleDependency of this.moduleDependencies.values()) {
318
+ if (category === "string" && moduleDependency.stringJsons.length !== 0) {
319
+ for (const jsonItem of moduleDependency.stringJsons) {
320
+ this.extracted(jsonItem, name);
321
+ }
322
+ } else if (category === "strarray" && moduleDependency.strArrayJsons.length !== 0) {
323
+ for (const jsonItem of moduleDependency.strArrayJsons) {
324
+ this.extracted(jsonItem, name);
325
+ }
326
+ } else if (category === "plural" && moduleDependency.pluralJsons.length !== 0) {
327
+ for (const jsonItem of moduleDependency.pluralJsons) {
328
+ this.extracted(jsonItem, name);
329
+ }
330
+ }
331
+ }
332
+ }
333
+ }
334
+ }
335
+ }
336
+ extracted(jsonItem, name) {
337
+ for (const descriptor of jsonItem.content["string"]) {
338
+ if (descriptor["name"] === name) {
339
+ let stringResourceItem = this.dependencyResourceMap.get(jsonItem.path);
340
+ if (!stringResourceItem) {
341
+ stringResourceItem = new Resource();
342
+ }
343
+ if (!stringResourceItem.resource.has(name)) {
344
+ stringResourceItem.resource.set(name, descriptor);
345
+ this.dependencyResourceMap.set(jsonItem.path, stringResourceItem);
346
+ }
347
+ return true;
348
+ }
349
+ }
350
+ return false;
351
+ }
352
+ analysisClassStructureTree(arkClass) {
353
+ let callgraph = this.callGraph;
354
+ let arkMethods = arkClass.getMethods();
355
+ for (let arkMethod of arkMethods) {
356
+ let methodStartLine = arkMethod.getLine();
357
+ let methodEndLine = this.getEndLine(methodStartLine, arkMethod.getCode());
358
+ let methodLineInterval = [];
359
+ methodLineInterval = [methodStartLine, methodEndLine];
360
+ if (!(methodLineInterval[0] > this.gitItem.lineInterval[1] || methodLineInterval[1] < this.gitItem.lineInterval[0])) {
361
+ let callStmtStartLine = arkMethod.getLine();
362
+ let callStmtEndLine = this.getEndLine(callStmtStartLine, arkMethod.getCode());
363
+ if (arkMethod.getName().startsWith("%AM")) {
364
+ const resolved = this.resolveAnonymousMethod(arkMethod, arkClass);
365
+ arkMethod = resolved.arkMethod;
366
+ callStmtStartLine = resolved.startLine;
367
+ callStmtEndLine = resolved.endLine;
368
+ }
369
+ if (this.isMethodValid(arkMethod)) {
370
+ for (let i = callStmtStartLine; i <= callStmtEndLine; i++) {
371
+ this.copiedLines[i - 1] = this.originLines[i - 1];
372
+ }
373
+ }
374
+ this.analysisMethodResource(arkMethod);
375
+ if (this.mode === "default") {
376
+ let invokeStmts = callgraph.getInvokeStmtByMethod(arkMethod.getSignature());
377
+ if (invokeStmts) {
378
+ for (let invokeStmt of invokeStmts) {
379
+ let dstMethod = invokeStmt.getCfg().getDeclaringMethod();
380
+ console.log(`[caller] ${arkMethod.getSignature()} is called by ${dstMethod.getSignature()} in ${dstMethod.getDeclaringArkClass().getDeclaringArkFile().getName()}`);
381
+ this.analysisCallArkFile(dstMethod, 1);
382
+ }
383
+ }
384
+ let setEdges = callgraph.getCallGraphNodeByMethod(arkMethod.getSignature()).getOutgoingEdges();
385
+ setEdges.forEach((edge) => {
386
+ let dst = edge.getDstNode();
387
+ if (dst instanceof CallGraphNode && !dst.isSdkMethod()) {
388
+ let dstMethodSignature = dst.getMethod();
389
+ let dstMethod = this.scene.getMethod(dstMethodSignature);
390
+ console.log(`[callee] ${arkMethod.getSignature()} calls ${dstMethodSignature} in ${dstMethod.getDeclaringArkClass().getDeclaringArkFile().getName()}`);
391
+ this.analysisCallArkFile(dstMethod, 1);
392
+ }
393
+ });
394
+ }
395
+ }
396
+ }
397
+ }
398
+ analysisCallArkFile = (arkMethod, currentDepth = 1) => {
399
+ let arkClass = arkMethod.getDeclaringArkClass();
400
+ let callArkFile = arkClass.getDeclaringArkFile();
401
+ let originLines = callArkFile.getCode().split("\n");
402
+ let copiedLines = this.dependencyFileDict.get(callArkFile);
403
+ if (!copiedLines) {
404
+ copiedLines = new Array(originLines.length);
405
+ this.dependencyFileDict.set(callArkFile, copiedLines);
406
+ }
407
+ if (arkClass.getName() != "%dlft") {
408
+ this.copyClassHeader(arkClass, copiedLines, originLines, true);
409
+ }
410
+ let callStmtStartLine = arkMethod.getLine();
411
+ let callStmtEndLine = this.getEndLine(callStmtStartLine, arkMethod.getCode());
412
+ if (arkMethod.getName().startsWith("%AM")) {
413
+ const resolved = this.resolveAnonymousMethod(arkMethod, arkClass);
414
+ arkMethod = resolved.arkMethod;
415
+ callStmtStartLine = resolved.startLine;
416
+ callStmtEndLine = resolved.endLine;
417
+ }
418
+ if (this.isMethodValid(arkMethod)) {
419
+ for (let i = callStmtStartLine; i <= callStmtEndLine; ++i) {
420
+ copiedLines[i - 1] = originLines[i - 1];
421
+ }
422
+ }
423
+ if (this.mode === "default" && currentDepth < this.maxCallDepth) {
424
+ let invokeStmts = this.callGraph.getInvokeStmtByMethod(arkMethod.getSignature());
425
+ if (invokeStmts) {
426
+ for (let invokeStmt of invokeStmts) {
427
+ let dstMethod = invokeStmt.getCfg().getDeclaringMethod();
428
+ console.log(`[caller][depth=${currentDepth}] ${arkMethod.getSignature()} is called by ${dstMethod.getSignature()} in ${dstMethod.getDeclaringArkClass().getDeclaringArkFile().getName()}`);
429
+ this.analysisCallArkFile(dstMethod, currentDepth + 1);
430
+ }
431
+ }
432
+ let setEdges = this.callGraph.getCallGraphNodeByMethod(arkMethod.getSignature()).getOutgoingEdges();
433
+ if (setEdges) {
434
+ setEdges.forEach((edge) => {
435
+ let dst = edge.getDstNode();
436
+ if (dst instanceof CallGraphNode && !dst.isSdkMethod()) {
437
+ let dstMethodSignature = dst.getMethod();
438
+ let dstMethod = this.scene.getMethod(dstMethodSignature);
439
+ console.log(`[callee][depth=${currentDepth}] ${arkMethod.getSignature()} calls ${dstMethodSignature} in ${dstMethod.getDeclaringArkClass().getDeclaringArkFile().getName()}`);
440
+ this.analysisCallArkFile(dstMethod, currentDepth + 1);
441
+ }
442
+ });
443
+ }
444
+ }
445
+ return copiedLines;
446
+ };
447
+ };
448
+
449
+ // src/context/git/GitDiffExtractor.ts
450
+ import simpleGit from "simple-git";
451
+ var ALLOWED_EXTS = [".ets", ".ts", ".c", ".cpp", ".h", ".hpp"];
452
+ function hasAllowedExt(filePath) {
453
+ if (!filePath) return false;
454
+ const lower = filePath.toLowerCase();
455
+ return ALLOWED_EXTS.some((ext) => lower.endsWith(ext));
456
+ }
457
+ function normalize(content) {
458
+ return content.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/-\n textPara/g, "-\n textPara");
459
+ }
460
+ function parseNameStatus(raw) {
461
+ const out = [];
462
+ if (!raw) return out;
463
+ for (const rawLine of raw.split("\n")) {
464
+ const line = rawLine.trim();
465
+ if (!line) continue;
466
+ const parts = line.split(" ");
467
+ const status = parts[0];
468
+ if (!status) continue;
469
+ if (status.startsWith("R") || status.startsWith("C")) {
470
+ out.push({ status, oldPath: parts[1] ?? "", newPath: parts[2] ?? "" });
471
+ } else if (status === "A") {
472
+ out.push({ status, oldPath: "", newPath: parts[1] ?? "" });
473
+ } else if (status === "D") {
474
+ out.push({ status, oldPath: parts[1] ?? "", newPath: "" });
475
+ } else {
476
+ out.push({ status, oldPath: parts[1] ?? "", newPath: parts[1] ?? "" });
477
+ }
478
+ }
479
+ return out;
480
+ }
481
+ function resolveFileMode(change) {
482
+ if (change.status.startsWith("R")) return "rename";
483
+ if (change.status === "A") return "add";
484
+ if (change.status === "D") return "delete";
485
+ return "modify";
486
+ }
487
+ function parseHunks(patch, newFilePath) {
488
+ const items = [];
489
+ if (!patch) return items;
490
+ const lines = patch.split("\n");
491
+ let current = new GitItem();
492
+ let oldLines = [];
493
+ let newLines = [];
494
+ for (const line of lines) {
495
+ if (line.startsWith("---") || line.startsWith("+++")) {
496
+ continue;
497
+ }
498
+ if (line.startsWith("@@")) {
499
+ current.contentBefore = normalize(oldLines.join("\n"));
500
+ current.contentAfter = normalize(newLines.join("\n"));
501
+ items.push(current);
502
+ current = new GitItem();
503
+ oldLines = [];
504
+ newLines = [];
505
+ const parts = line.split(" ");
506
+ const oldInfo = parts[1].substring(1).split(",");
507
+ const oldRow = parseInt(oldInfo[0], 10);
508
+ const oldAffected = oldInfo.length === 2 ? parseInt(oldInfo[1], 10) : 1;
509
+ const newInfo = parts[2].substring(1).split(",");
510
+ const newRow = parseInt(newInfo[0], 10);
511
+ const newAffected = newInfo.length === 2 ? parseInt(newInfo[1], 10) : 1;
512
+ current.path = newFilePath;
513
+ current.lineInterval = [newRow, newRow + newAffected - 1];
514
+ if (oldAffected === 0) current.mode = "add";
515
+ if (newAffected === 0) {
516
+ current.mode = "delete";
517
+ } else {
518
+ current.mode = "modify";
519
+ }
520
+ } else if (line.startsWith("-")) {
521
+ oldLines.push(line.substring(1));
522
+ } else if (line.startsWith("+")) {
523
+ newLines.push(line.substring(1));
524
+ }
525
+ }
526
+ current.contentBefore = normalize(oldLines.join("\n"));
527
+ current.contentAfter = normalize(newLines.join("\n"));
528
+ items.push(current);
529
+ items.shift();
530
+ return items;
531
+ }
532
+ async function extractCommitDiff(repoPath, commitId) {
533
+ const git = simpleGit(repoPath);
534
+ const parentId = (await git.raw(["rev-parse", `${commitId}^`])).trim();
535
+ console.log(`commit: ${commitId} parent: ${parentId}`);
536
+ const nameStatus = await git.raw(["diff", "--name-status", "-M", parentId, commitId]);
537
+ const changes = parseNameStatus(nameStatus);
538
+ const results = [];
539
+ for (const change of changes) {
540
+ if (change.oldPath && !hasAllowedExt(change.oldPath)) continue;
541
+ if (change.newPath && !hasAllowedExt(change.newPath)) continue;
542
+ const reportPath = change.newPath || change.oldPath;
543
+ const filePaths = [change.oldPath, change.newPath].filter((p) => p);
544
+ const patchArgs = ["diff", "-U0", "-M", parentId, commitId, "--", ...new Set(filePaths)];
545
+ const patch = await git.raw(patchArgs);
546
+ const hunkItems = parseHunks(patch, reportPath);
547
+ const fileMode = resolveFileMode(change);
548
+ for (const item of hunkItems) {
549
+ item.file_mode = fileMode;
550
+ }
551
+ results.push(...hunkItems);
552
+ }
553
+ return results;
554
+ }
555
+
556
+ // src/context/index.ts
557
+ var __filename = fileURLToPath(import.meta.url);
558
+ var __dirname = path3.dirname(__filename);
559
+ function linesToRanges(lines) {
560
+ const ranges = [];
561
+ let start = -1;
562
+ for (let i = 0; i < lines.length; i++) {
563
+ const defined = lines[i] !== void 0;
564
+ if (defined && start === -1) {
565
+ start = i + 1;
566
+ } else if (!defined && start !== -1) {
567
+ ranges.push([start, i]);
568
+ start = -1;
569
+ }
570
+ }
571
+ if (start !== -1) {
572
+ ranges.push([start, lines.length]);
573
+ }
574
+ return ranges;
575
+ }
576
+ function resolveSdkConfigPath() {
577
+ return path3.resolve(__dirname, "resources", "sdkConfig.json");
578
+ }
579
+ function resolveResourcesRoot() {
580
+ return path3.resolve(__dirname, "resources");
581
+ }
582
+ function loadSdks(ohosSdkPath, hmsSdkPath) {
583
+ const sdkConfigPath = resolveSdkConfigPath();
584
+ if (!fs3.existsSync(sdkConfigPath)) {
585
+ throw new Error(`HomeTrans context: sdkConfig.json not found at ${sdkConfigPath}`);
586
+ }
587
+ const configurations = JSON.parse(fs3.readFileSync(sdkConfigPath, "utf-8"));
588
+ const sdks = configurations.sdks ?? [];
589
+ const resourcesRoot = resolveResourcesRoot();
590
+ sdks.forEach((sdk) => {
591
+ if (sdk.name === "ohosSdk") {
592
+ sdk.path = ohosSdkPath;
593
+ } else if (sdk.name === "hmsSdk") {
594
+ sdk.path = hmsSdkPath;
595
+ } else {
596
+ const stripped = sdk.path.replace(/^\.\/?resources[\\/]?/, "");
597
+ sdk.path = path3.join(resourcesRoot, stripped);
598
+ }
599
+ });
600
+ return sdks;
601
+ }
602
+ async function extractCommitContext(input) {
603
+ const { projectPath, commitId } = input;
604
+ const mode = input.mode ?? "default";
605
+ const ohosSdkPath = input.ohosSdkPath ?? process.env.HOMETRANS_OHOS_SDK_PATH ?? "";
606
+ const hmsSdkPath = input.hmsSdkPath ?? process.env.HOMETRANS_HMS_SDK_PATH ?? "";
607
+ if (!projectPath) {
608
+ throw new Error("extractCommitContext: projectPath is required");
609
+ }
610
+ if (!commitId) {
611
+ throw new Error("extractCommitContext: commitId is required");
612
+ }
613
+ if (!ohosSdkPath && !hmsSdkPath) {
614
+ throw new Error(
615
+ "extractCommitContext: ohosSdkPath/hmsSdkPath not provided. Pass them as tool input or set HOMETRANS_OHOS_SDK_PATH / HOMETRANS_HMS_SDK_PATH env vars (e.g. D:\\DevEco Studio\\sdk\\default\\openharmony\\ets)."
616
+ );
617
+ }
618
+ console.error(
619
+ `[hometrans/context] analyzing commit=${commitId} project=${projectPath} mode=${mode}`
620
+ );
621
+ const gitItems = await extractCommitDiff(projectPath, commitId);
622
+ console.error(`[hometrans/context] extracted ${gitItems.length} diff items`);
623
+ const scene = new Scene2();
624
+ const analysisResult = [];
625
+ const sceneConfig = new SceneConfig({ enableAST: false });
626
+ const sdkList = loadSdks(ohosSdkPath, hmsSdkPath);
627
+ const fileList = getAllFiles(projectPath, [".ts", ".ets", ".js", ".json5"]);
628
+ sceneConfig.buildFromProjectFiles("hometrans_target", projectPath, fileList, sdkList);
629
+ scene.buildSceneFromFiles(sceneConfig);
630
+ scene.inferTypes();
631
+ const callGraph = new CallGraph2(scene);
632
+ if (mode === "default") {
633
+ const callGraphBuilder = new CallGraphBuilder(callGraph, scene);
634
+ const entryPoints = [];
635
+ const totalMethodSignatures = scene.getFiles().filter(
636
+ (arkFile) => arkFile.getFilePath().endsWith(".ets") || arkFile.getFilePath().endsWith(".ts")
637
+ ).flatMap((arkFile) => arkFile.getClasses()).flatMap((arkClass) => arkClass.getMethods()).map((arkMethod) => arkMethod.getSignature());
638
+ safePush(entryPoints, totalMethodSignatures);
639
+ callGraphBuilder.buildClassHierarchyCallGraph(entryPoints, false);
640
+ }
641
+ const arkFiles = scene.getFiles();
642
+ const gitAnalysis = new ArkTsGitInfoAnalysis();
643
+ for (const gitItem of gitItems) {
644
+ for (const arkFile of arkFiles) {
645
+ let paths = gitItem.path.split("/");
646
+ if (gitItem.path.startsWith("NewPhotos")) {
647
+ paths = paths.slice(1);
648
+ paths.unshift(arkFile.getProjectDir());
649
+ } else {
650
+ paths.unshift(arkFile.getProjectDir());
651
+ }
652
+ const gitFilePath = getFilePath(paths);
653
+ if (arkFile.getFilePath() === gitFilePath) {
654
+ gitAnalysis.analysis(arkFile, gitItem, callGraph, mode);
655
+ }
656
+ }
657
+ }
658
+ const dependencyResourceMap = gitAnalysis.dependencyResourceMap;
659
+ dependencyResourceMap.forEach((resource, filePath) => {
660
+ const base = path3.basename(filePath);
661
+ if (base === "string.json" || base === "plural.json" || base === "strarray.json") {
662
+ analysisResult.push({
663
+ path: filePath,
664
+ kind: "resource",
665
+ resourceNames: Array.from(resource.resource.keys())
666
+ });
667
+ }
668
+ });
669
+ const dependencyFileDict = gitAnalysis.dependencyFileDict;
670
+ dependencyFileDict.forEach((lines, arkFile) => {
671
+ const ranges = linesToRanges(lines);
672
+ if (ranges.length === 0) {
673
+ return;
674
+ }
675
+ analysisResult.push({
676
+ path: arkFile.getFilePath(),
677
+ kind: "source",
678
+ ranges
679
+ });
680
+ });
681
+ console.error(
682
+ `[hometrans/context] produced ${analysisResult.length} context entries`
683
+ );
684
+ return analysisResult;
685
+ }
686
+ export {
687
+ extractCommitContext
688
+ };