arkanalyzer 1.0.92 → 1.0.94

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 (67) hide show
  1. package/lib/callgraph/algorithm/AbstractAnalysis.d.ts +6 -0
  2. package/lib/callgraph/algorithm/AbstractAnalysis.d.ts.map +1 -1
  3. package/lib/callgraph/algorithm/AbstractAnalysis.js +17 -8
  4. package/lib/callgraph/algorithm/ClassHierarchyAnalysis.d.ts +1 -0
  5. package/lib/callgraph/algorithm/ClassHierarchyAnalysis.d.ts.map +1 -1
  6. package/lib/callgraph/algorithm/ClassHierarchyAnalysis.js +9 -3
  7. package/lib/callgraph/algorithm/RapidTypeAnalysis.d.ts +147 -0
  8. package/lib/callgraph/algorithm/RapidTypeAnalysis.d.ts.map +1 -1
  9. package/lib/callgraph/algorithm/RapidTypeAnalysis.js +802 -76
  10. package/lib/callgraph/model/CallGraph.d.ts +1 -0
  11. package/lib/callgraph/model/CallGraph.d.ts.map +1 -1
  12. package/lib/callgraph/model/CallGraph.js +4 -0
  13. package/lib/callgraph/model/CallSite.d.ts +88 -1
  14. package/lib/callgraph/model/CallSite.d.ts.map +1 -1
  15. package/lib/callgraph/model/CallSite.js +58 -4
  16. package/lib/core/common/ArkIRTransformer.d.ts.map +1 -1
  17. package/lib/core/common/ArkIRTransformer.js +1 -1
  18. package/lib/core/common/ArkValueTransformer.d.ts.map +1 -1
  19. package/lib/core/common/ArkValueTransformer.js +3 -1
  20. package/lib/core/common/IRInference.js +2 -2
  21. package/lib/core/common/SdkUtils.d.ts.map +1 -1
  22. package/lib/core/common/SdkUtils.js +22 -6
  23. package/lib/core/common/TypeInference.d.ts.map +1 -1
  24. package/lib/core/common/TypeInference.js +3 -0
  25. package/lib/core/graph/builder/SwitchBuilder.d.ts.map +1 -1
  26. package/lib/core/graph/builder/SwitchBuilder.js +11 -1
  27. package/lib/core/model/builder/ArkClassBuilder.js +7 -6
  28. package/lib/core/model/builder/ArkMethodBuilder.d.ts +2 -1
  29. package/lib/core/model/builder/ArkMethodBuilder.d.ts.map +1 -1
  30. package/lib/core/model/builder/ArkMethodBuilder.js +5 -1
  31. package/lib/frontend/cppFrontend/graph/builder/SwitchBuilder.d.ts.map +1 -1
  32. package/lib/frontend/cppFrontend/graph/builder/SwitchBuilder.js +11 -1
  33. package/package.json +1 -1
  34. package/lib/node_modules/ohos-typescript/README.OpenSource +0 -11
  35. package/lib/node_modules/ohos-typescript/README.md +0 -81
  36. package/lib/node_modules/ohos-typescript/SECURITY.md +0 -41
  37. package/lib/node_modules/ohos-typescript/ThirdPartyNoticeText.txt +0 -193
  38. package/lib/node_modules/ohos-typescript/bin/tsc +0 -2
  39. package/lib/node_modules/ohos-typescript/bin/tsserver +0 -2
  40. package/lib/node_modules/ohos-typescript/lib/README.md +0 -5
  41. package/lib/node_modules/ohos-typescript/lib/cancellationToken.js +0 -86
  42. package/lib/node_modules/ohos-typescript/lib/cs/diagnosticMessages.generated.json +0 -1880
  43. package/lib/node_modules/ohos-typescript/lib/de/diagnosticMessages.generated.json +0 -1880
  44. package/lib/node_modules/ohos-typescript/lib/es/diagnosticMessages.generated.json +0 -1880
  45. package/lib/node_modules/ohos-typescript/lib/fr/diagnosticMessages.generated.json +0 -1880
  46. package/lib/node_modules/ohos-typescript/lib/it/diagnosticMessages.generated.json +0 -1880
  47. package/lib/node_modules/ohos-typescript/lib/ja/diagnosticMessages.generated.json +0 -1880
  48. package/lib/node_modules/ohos-typescript/lib/ko/diagnosticMessages.generated.json +0 -1880
  49. package/lib/node_modules/ohos-typescript/lib/lib.dom.d.ts +0 -18505
  50. package/lib/node_modules/ohos-typescript/lib/lib.dom.iterable.d.ts +0 -341
  51. package/lib/node_modules/ohos-typescript/lib/lib.scripthost.d.ts +0 -327
  52. package/lib/node_modules/ohos-typescript/lib/lib.webworker.d.ts +0 -6192
  53. package/lib/node_modules/ohos-typescript/lib/lib.webworker.importscripts.d.ts +0 -26
  54. package/lib/node_modules/ohos-typescript/lib/lib.webworker.iterable.d.ts +0 -175
  55. package/lib/node_modules/ohos-typescript/lib/pl/diagnosticMessages.generated.json +0 -1880
  56. package/lib/node_modules/ohos-typescript/lib/pt-br/diagnosticMessages.generated.json +0 -1880
  57. package/lib/node_modules/ohos-typescript/lib/ru/diagnosticMessages.generated.json +0 -1880
  58. package/lib/node_modules/ohos-typescript/lib/tr/diagnosticMessages.generated.json +0 -1880
  59. package/lib/node_modules/ohos-typescript/lib/tsc.js +0 -114551
  60. package/lib/node_modules/ohos-typescript/lib/tsserver.js +0 -179691
  61. package/lib/node_modules/ohos-typescript/lib/tsserverlibrary.d.ts +0 -13648
  62. package/lib/node_modules/ohos-typescript/lib/tsserverlibrary.js +0 -178688
  63. package/lib/node_modules/ohos-typescript/lib/typesMap.json +0 -497
  64. package/lib/node_modules/ohos-typescript/lib/typingsInstaller.js +0 -39871
  65. package/lib/node_modules/ohos-typescript/lib/watchGuard.js +0 -49
  66. package/lib/node_modules/ohos-typescript/lib/zh-cn/diagnosticMessages.generated.json +0 -1880
  67. package/lib/node_modules/ohos-typescript/lib/zh-tw/diagnosticMessages.generated.json +0 -1880
@@ -40,25 +40,58 @@ Object.defineProperty(exports, "__esModule", { value: true });
40
40
  exports.RapidTypeAnalysis = void 0;
41
41
  const Expr_1 = require("../../core/base/Expr");
42
42
  const Stmt_1 = require("../../core/base/Stmt");
43
+ const ArkClass_1 = require("../../core/model/ArkClass");
44
+ const CallGraph_1 = require("../model/CallGraph");
43
45
  const AbstractAnalysis_1 = require("./AbstractAnalysis");
44
46
  const logger_1 = __importStar(require("../../utils/logger"));
47
+ const Type_1 = require("../../core/base/Type");
45
48
  const TSConst_1 = require("../../core/common/TSConst");
46
49
  const Local_1 = require("../../core/base/Local");
47
50
  const Const_1 = require("../../core/common/Const");
51
+ const SdkUtils_1 = require("../../core/common/SdkUtils");
52
+ const Ref_1 = require("../../core/base/Ref");
48
53
  const logger = logger_1.default.getLogger(logger_1.LOG_MODULE_TYPE.ARKANALYZER, 'RTA');
54
+ const BUILTIN_ARRAY_CLASS_NAME = 'Array';
55
+ /**
56
+ * Built-in container APIs that may take FunctionType args but only store them
57
+ * (do not invoke). These must not get virtual callback CallSites.
58
+ * All other no-CFG built-ins default to synthesizing callback edges (forEach/map/then/...).
59
+ */
60
+ const BUILTIN_CONTAINER_NON_INVOKING_METHODS = new Map([
61
+ ['Array', new Set(['push', 'unshift', 'splice', 'indexOf', 'lastIndexOf', 'includes'])],
62
+ ['Map', new Set(['set', 'get', 'has', 'delete'])],
63
+ ['Set', new Set(['add', 'has', 'delete'])],
64
+ ['WeakMap', new Set(['set', 'get', 'has', 'delete'])],
65
+ ['WeakSet', new Set(['add', 'has', 'delete'])],
66
+ ]);
67
+ const NON_INVOKING_METHOD_NAMES = new Set(Array.from(BUILTIN_CONTAINER_NON_INVOKING_METHODS.values()).flatMap(s => Array.from(s)));
68
+ const CALLBACK_BINDING_ARG_BITS = 8;
69
+ const CALLBACK_BINDING_ARG_STRIDE = 1 << CALLBACK_BINDING_ARG_BITS;
49
70
  class RapidTypeAnalysis extends AbstractAnalysis_1.AbstractAnalysis {
50
71
  constructor(scene, cg, cb, enableThisPrune = false) {
51
72
  super(scene, cg);
52
73
  // TODO: signature duplicated check
53
74
  this.instancedClasses = new Set();
54
- // TODO: Set duplicated check
55
75
  this.ignoredCalls = new Map();
56
76
  this.virtualDispatchIndex = new Map();
77
+ // ownerFuncID → logical argument index → lazily-created binding state
78
+ this.callbackBindings = new Map();
79
+ // FieldSignature → callback targets/subscribers. This is intentionally type-based,
80
+ // not allocation-site-sensitive, to keep field callback propagation lightweight.
81
+ this.callbackFieldBindings = new Map();
82
+ // Reused scratch storage for closure-chain cycle detection.
83
+ this.closureTraceVisited = new Set();
84
+ // Reused scratch for AliasType peeling in unwrapFunctionType.
85
+ this.aliasTypeUnwrapVisited = new Set();
86
+ // Reused scratch list of executable callee FuncIDs for one resolveCall.
87
+ this.executableCalleeScratch = [];
88
+ // Whether getBuiltinArrayClassSignature has completed its one-time lookup (hit or miss).
89
+ this.builtinArrayClassResolved = false;
57
90
  this.cgBuilder = cb;
58
91
  this.enableThisPrune = enableThisPrune;
59
92
  }
60
93
  init() {
61
- this.scene.getClasses().filter(c => c.getName() === Const_1.DEFAULT_ARK_CLASS_NAME).forEach(c => {
94
+ this.scene.getClasses().filter((c) => c.getName() === Const_1.DEFAULT_ARK_CLASS_NAME).forEach((c) => {
62
95
  var _a;
63
96
  const dfltMethod = (_a = c.getMethodWithName(Const_1.DEFAULT_ARK_METHOD_NAME)) === null || _a === void 0 ? void 0 : _a.getSignature();
64
97
  if (dfltMethod) {
@@ -69,63 +102,717 @@ class RapidTypeAnalysis extends AbstractAnalysis_1.AbstractAnalysis {
69
102
  super.init();
70
103
  }
71
104
  resolveCall(callerMethod, invokeStmt) {
72
- let invokeExpr = invokeStmt.getInvokeExpr();
73
- let resolveResult = [];
105
+ const invokeExpr = invokeStmt.getInvokeExpr();
74
106
  if (!invokeExpr) {
75
107
  return [];
76
108
  }
77
- // process anonymous method call
78
- this.getParamAnonymousMethod(invokeExpr).forEach(method => {
79
- resolveResult.push(this.cg.getCallSiteManager().newCallSite(invokeStmt, undefined, this.cg.getCallGraphNodeByMethod(method).getID(), callerMethod));
80
- });
81
- let calleeMethod = this.resolveInvokeExpr(invokeExpr);
109
+ if (invokeExpr instanceof Expr_1.ArkPtrInvokeExpr) {
110
+ return this.resolvePtrInvoke(callerMethod, invokeStmt, invokeExpr);
111
+ }
112
+ const resolveResult = [];
113
+ const declared = this.resolveInvokeExpr(invokeExpr);
114
+ this.resolveDirectOrVirtual(callerMethod, invokeStmt, invokeExpr, declared, resolveResult);
115
+ this.processCallbackArguments(callerMethod, invokeStmt, invokeExpr, declared, resolveResult);
116
+ return resolveResult;
117
+ }
118
+ resolveDirectOrVirtual(callerMethod, invokeStmt, invokeExpr, calleeMethod, resolveResult) {
82
119
  if (!calleeMethod) {
83
- return resolveResult;
120
+ return;
84
121
  }
85
122
  if (invokeExpr instanceof Expr_1.ArkStaticInvokeExpr) {
86
- // get specific method
87
- resolveResult.push(this.cg.getCallSiteManager().newCallSite(invokeStmt, undefined, this.cg.getCallGraphNodeByMethod(calleeMethod.getSignature()).getID(), callerMethod));
88
- }
89
- else {
90
- let declareClass = calleeMethod.getDeclaringArkClass();
91
- const methodName = calleeMethod.getName();
92
- // Aggressive heuristic: when enabled and the call is `this.foo()`,
93
- // only keep the implementation of `foo` in the current declaring class,
94
- // or the first superclass that defines it (e.g. B.m() calling this.foo() → A.foo() when B extends A).
95
- const prunedCallSite = this.tryResolveAggressiveThisPruneCall(invokeStmt, methodName, callerMethod);
96
- if (prunedCallSite) {
97
- resolveResult.push(prunedCallSite);
98
- return resolveResult;
99
- }
100
- const directCallSite = this.resolveConstructorClassFromNew(methodName, invokeStmt, callerMethod);
101
- if (directCallSite) {
102
- resolveResult.push(directCallSite);
103
- return resolveResult;
104
- }
105
- // Private methods cannot be overridden; only the declaring class has an implementation.
106
- if (calleeMethod.isPrivate()) {
107
- const calleeNode = this.cg.getCallGraphNodeByMethod(calleeMethod.getSignature());
108
- if (this.instancedClasses.has(declareClass.getSignature())) {
109
- resolveResult.push(this.cg.getCallSiteManager().newCallSite(invokeStmt, undefined, calleeNode.getID(), callerMethod));
110
- }
111
- else {
112
- this.addIgnoredCalls(declareClass.getSignature(), callerMethod, calleeNode.getID(), invokeStmt);
123
+ resolveResult.push(this.newRTACallSite(invokeStmt, callerMethod, this.cg.getCallGraphNodeByMethod(calleeMethod.getSignature()).getID()));
124
+ return;
125
+ }
126
+ const declareClass = calleeMethod.getDeclaringArkClass();
127
+ const methodName = calleeMethod.getName();
128
+ // Aggressive heuristic: when enabled and the call is `this.foo()`,
129
+ // only keep the implementation of `foo` in the current declaring class,
130
+ // or the first superclass that defines it (e.g. B.m() calling this.foo() → A.foo() when B extends A).
131
+ const prunedCallSite = this.tryResolveAggressiveThisPruneCall(invokeStmt, methodName, callerMethod);
132
+ if (prunedCallSite) {
133
+ resolveResult.push(prunedCallSite);
134
+ return;
135
+ }
136
+ const directCallSite = this.resolveConstructorClassFromNew(methodName, invokeStmt, callerMethod);
137
+ if (directCallSite) {
138
+ resolveResult.push(directCallSite);
139
+ return;
140
+ }
141
+ // Private methods cannot be overridden; only the declaring class has an implementation.
142
+ if (calleeMethod.isPrivate()) {
143
+ const calleeNode = this.cg.getCallGraphNodeByMethod(calleeMethod.getSignature());
144
+ if (this.instancedClasses.has(declareClass.getSignature())) {
145
+ resolveResult.push(this.newRTACallSite(invokeStmt, callerMethod, calleeNode.getID()));
146
+ }
147
+ else {
148
+ this.addIgnoredCalls(declareClass.getSignature(), callerMethod, calleeNode.getID(), invokeStmt);
149
+ }
150
+ return;
151
+ }
152
+ const virtualDispatchRows = this.getOrBuildVirtualDispatchRows(declareClass, methodName);
153
+ for (let i = 0; i < virtualDispatchRows.classSignatures.length; i++) {
154
+ const classSignature = virtualDispatchRows.classSignatures[i];
155
+ if (virtualDispatchRows.isUnconditionallyReachable[i] || this.instancedClasses.has(classSignature)) {
156
+ resolveResult.push(this.newRTACallSite(invokeStmt, callerMethod, virtualDispatchRows.calleeNodeIDs[i]));
157
+ }
158
+ else {
159
+ this.addIgnoredCalls(classSignature, callerMethod, virtualDispatchRows.calleeNodeIDs[i], invokeStmt);
160
+ }
161
+ }
162
+ }
163
+ /**
164
+ * Single args scan:
165
+ * - if any executable real callee exists (or declared callee has CFG), record callback bindings
166
+ * (concrete FunctionType) or forward parameter bindings into callee slots;
167
+ * - otherwise, for no-CFG SDK / built-in / unresolved (`%unk`) callees, append virtual
168
+ * callback CallSites for concrete FunctionType args, parameter bindings, and callback
169
+ * fields.
170
+ */
171
+ processCallbackArguments(callerMethod, invokeStmt, invokeExpr, declared, resolveResult) {
172
+ const hasVirtualCallbackCallee = this.scanExecutableCallees(resolveResult);
173
+ const hasExecutableTarget = this.executableCalleeScratch.length > 0;
174
+ const declaredHasCfg = declared !== undefined && declared.getCfg() !== undefined;
175
+ if (!hasExecutableTarget && !declaredHasCfg) {
176
+ this.handleNoExecutableCalleePath(callerMethod, invokeStmt, declared, hasVirtualCallbackCallee, resolveResult);
177
+ return;
178
+ }
179
+ let declaredOwnerFuncID;
180
+ if (declared && !hasExecutableTarget) {
181
+ declaredOwnerFuncID = this.cg.getCallGraphNodeByMethod(declared.getSignature()).getID();
182
+ }
183
+ const args = invokeExpr.getArgs();
184
+ const callerArkMethod = this.cg.getArkMethodByFuncID(callerMethod);
185
+ for (let i = 0; i < args.length; i++) {
186
+ const arg = args[i];
187
+ const functionType = this.unwrapFunctionType(arg.getType());
188
+ if (!functionType && !this.isCallbackType(arg.getType())) {
189
+ continue;
190
+ }
191
+ if (functionType) {
192
+ this.bindFunctionTypeCallback(functionType, i, hasExecutableTarget, declaredOwnerFuncID);
193
+ }
194
+ else {
195
+ this.bindClassTypeCallback(arg, i, callerArkMethod, hasExecutableTarget, declaredOwnerFuncID);
196
+ }
197
+ // Parameter forwarding: start(cb) { this.schedule(cb); }
198
+ // Propagate caller's parameter binding into each callee's argument slot i.
199
+ if (!(arg instanceof Local_1.Local) || !callerArkMethod) {
200
+ continue;
201
+ }
202
+ const sourceKey = this.resolveFuncPtrToBindingKey(arg, callerArkMethod);
203
+ if (sourceKey === undefined) {
204
+ continue;
205
+ }
206
+ if (hasExecutableTarget) {
207
+ for (let j = 0; j < this.executableCalleeScratch.length; j++) {
208
+ this.linkCallbackBinding(sourceKey, this.encodeCallbackBindingKey(this.executableCalleeScratch[j], i));
113
209
  }
114
- return resolveResult;
115
210
  }
116
- const virtualDispatchRows = this.getOrBuildVirtualDispatchRows(declareClass, methodName);
117
- for (let i = 0; i < virtualDispatchRows.classSignatures.length; i++) {
118
- const classSignature = virtualDispatchRows.classSignatures[i];
119
- if (virtualDispatchRows.isSdkClasses[i] || this.instancedClasses.has(classSignature)) {
120
- resolveResult.push(this.cg.getCallSiteManager().newCallSite(invokeStmt, undefined, virtualDispatchRows.calleeNodeIDs[i], callerMethod));
211
+ else if (declaredOwnerFuncID !== undefined) {
212
+ this.linkCallbackBinding(sourceKey, this.encodeCallbackBindingKey(declaredOwnerFuncID, i));
213
+ }
214
+ }
215
+ }
216
+ /**
217
+ * Bind a FunctionType callback argument to the callee's parameter binding slot.
218
+ * Extracted from processCallbackArguments for readability.
219
+ */
220
+ bindFunctionTypeCallback(functionType, argIndex, hasExecutableTarget, declaredOwnerFuncID) {
221
+ const callbackMethod = this.scene.getMethod(functionType.getMethodSignature());
222
+ if (callbackMethod && callbackMethod.getCfg()) {
223
+ const targetFuncID = this.cg.getCallGraphNodeByMethod(callbackMethod.getSignature()).getID();
224
+ if (hasExecutableTarget) {
225
+ for (let j = 0; j < this.executableCalleeScratch.length; j++) {
226
+ this.addCallbackTarget(this.executableCalleeScratch[j], argIndex, targetFuncID);
121
227
  }
122
- else {
123
- this.addIgnoredCalls(classSignature, callerMethod, virtualDispatchRows.calleeNodeIDs[i], invokeStmt);
228
+ }
229
+ else if (declaredOwnerFuncID !== undefined) {
230
+ this.addCallbackTarget(declaredOwnerFuncID, argIndex, targetFuncID);
231
+ }
232
+ }
233
+ }
234
+ /**
235
+ * Bind a ClassType callback (Callback<T>, AsyncCallback<T>, ...) argument to the
236
+ * callee's parameter binding slot via field binding state.
237
+ * Extracted from processCallbackArguments for readability.
238
+ */
239
+ bindClassTypeCallback(arg, argIndex, callerArkMethod, hasExecutableTarget, declaredOwnerFuncID) {
240
+ let fieldRef;
241
+ if (arg instanceof Ref_1.ArkInstanceFieldRef) {
242
+ fieldRef = arg;
243
+ }
244
+ else if (arg instanceof Local_1.Local && callerArkMethod) {
245
+ fieldRef = this.resolveFuncPtrToFieldRef(arg, callerArkMethod);
246
+ }
247
+ if (fieldRef) {
248
+ const fieldKey = this.getCallbackFieldKey(fieldRef);
249
+ if (hasExecutableTarget) {
250
+ for (let j = 0; j < this.executableCalleeScratch.length; j++) {
251
+ const bindingKey = this.encodeCallbackBindingKey(this.executableCalleeScratch[j], argIndex);
252
+ this.linkFieldToBinding(fieldKey, bindingKey);
124
253
  }
125
254
  }
255
+ else if (declaredOwnerFuncID !== undefined) {
256
+ const bindingKey = this.encodeCallbackBindingKey(declaredOwnerFuncID, argIndex);
257
+ this.linkFieldToBinding(fieldKey, bindingKey);
258
+ }
259
+ }
260
+ }
261
+ scanExecutableCallees(resolveResult) {
262
+ this.executableCalleeScratch.length = 0;
263
+ let hasVirtualCallbackCallee = false;
264
+ for (let i = 0; i < resolveResult.length; i++) {
265
+ const calleeId = resolveResult[i].getCalleeFuncID();
266
+ if (calleeId === undefined) {
267
+ continue;
268
+ }
269
+ const method = this.cg.getArkMethodByFuncID(calleeId);
270
+ if (method && method.getCfg()) {
271
+ this.executableCalleeScratch.push(calleeId);
272
+ }
273
+ else if (this.shouldSynthesizeVirtualCallback(calleeId)) {
274
+ hasVirtualCallbackCallee = true;
275
+ }
126
276
  }
277
+ return hasVirtualCallbackCallee;
278
+ }
279
+ handleNoExecutableCalleePath(callerMethod, invokeStmt, declared, hasVirtualCallbackCallee, resolveResult) {
280
+ const invokeExpr = invokeStmt.getInvokeExpr();
281
+ if (!invokeExpr) {
282
+ return;
283
+ }
284
+ const args = invokeExpr.getArgs();
285
+ let shouldAppendVirtual = hasVirtualCallbackCallee;
286
+ if (!shouldAppendVirtual && declared) {
287
+ const declaredOwnerFuncID = this.cg.getCallGraphNodeByMethod(declared.getSignature()).getID();
288
+ shouldAppendVirtual = this.shouldSynthesizeVirtualCallback(declaredOwnerFuncID);
289
+ }
290
+ // Declaring signature may be `@%unk/%unk:...` with no Scene method / no CFG node that
291
+ // passes the SDK/built-in gates; still synthesize callback edges from the invoke itself.
292
+ if (!shouldAppendVirtual) {
293
+ shouldAppendVirtual = this.shouldSynthesizeVirtualCallbackFromInvoke(invokeExpr);
294
+ }
295
+ if (shouldAppendVirtual) {
296
+ this.appendVirtualCallbackCallSites(callerMethod, invokeStmt, args, resolveResult);
297
+ }
298
+ }
299
+ /**
300
+ * Unresolved declaring file (`@%unk/%unk:...`) — common when type inference fails for
301
+ * built-ins such as Promise.constructor in large apps.
302
+ */
303
+ isUnresolvedProjectName(projectName) {
304
+ return projectName === '%unk';
305
+ }
306
+ /**
307
+ * Built-in container APIs that accept FunctionType values without invoking them
308
+ * (writes such as push/set, and lookups such as indexOf/has). Caller must already
309
+ * know the method is a `@built-in` SDK method.
310
+ */
311
+ isBuiltInContainerNonInvokingMethod(method) {
312
+ if (!method) {
313
+ return false;
314
+ }
315
+ const nonInvokingNames = BUILTIN_CONTAINER_NON_INVOKING_METHODS.get(method.getDeclaringArkClass().getName());
316
+ if (!nonInvokingNames) {
317
+ return false;
318
+ }
319
+ return nonInvokingNames.has(method.getName());
320
+ }
321
+ isBuiltInContainerNonInvokingMethodByMethodName(methodName) {
322
+ return NON_INVOKING_METHOD_NAMES.has(methodName);
323
+ }
324
+ /**
325
+ * Whether a no-CFG callee should synthesize virtual edges into callback args.
326
+ * OH SDK and unresolved (`%unk`) APIs always qualify. Built-ins qualify by default
327
+ * except container non-invoking APIs (Array.push / Array.indexOf / Map.set / ...).
328
+ * Resolves the CG node and declaring projectName once.
329
+ */
330
+ shouldSynthesizeVirtualCallback(id) {
331
+ const node = this.cg.getNode(id);
332
+ if (!node) {
333
+ return false;
334
+ }
335
+ const projectName = node.getMethod().getDeclaringClassSignature().getDeclaringFileSignature().getProjectName();
336
+ // Same order as previous isOHSdkMethod → isUnresolved → isBuiltIn path.
337
+ if (node.isSdkMethod() && projectName !== SdkUtils_1.SdkUtils.BUILT_IN_NAME) {
338
+ return true;
339
+ }
340
+ if (this.isUnresolvedProjectName(projectName)) {
341
+ return !this.isBuiltInContainerNonInvokingMethodByMethodName(node.getMethod().getMethodSubSignature().getMethodName());
342
+ }
343
+ if (node.isSdkMethod() && projectName === SdkUtils_1.SdkUtils.BUILT_IN_NAME) {
344
+ return !this.isBuiltInContainerNonInvokingMethod(this.cg.getArkMethodByFuncID(id));
345
+ }
346
+ return false;
347
+ }
348
+ /**
349
+ * Invoke-site gate when {@link resolveInvokeExpr} cannot produce a usable declared method
350
+ * (e.g. `@%unk/%unk: Promise.constructor` with no Scene body).
351
+ */
352
+ shouldSynthesizeVirtualCallbackFromInvoke(invokeExpr) {
353
+ const projectName = invokeExpr.getMethodSignature().getDeclaringClassSignature().getDeclaringFileSignature().getProjectName();
354
+ if (!this.isUnresolvedProjectName(projectName)) {
355
+ return false;
356
+ }
357
+ return !this.isBuiltInContainerNonInvokingMethodByMethodName(invokeExpr.getMethodSignature().getMethodSubSignature().getMethodName());
358
+ }
359
+ /**
360
+ * No-CFG callee that invokes callbacks: emit virtual edges into every callback-shaped argument.
361
+ * Caller must have already gated on {@link shouldSynthesizeVirtualCallback}. Built-in container
362
+ * non-invoking APIs (Array.push / Array.indexOf / Map.set / ...) remain excluded by that gate.
363
+ */
364
+ appendVirtualCallbackCallSites(callerMethod, invokeStmt, args, resolveResult) {
365
+ for (let i = 0; i < args.length; i++) {
366
+ this.appendCallbackValueCallSites(args[i], invokeStmt, callerMethod, resolveResult, i);
367
+ }
368
+ }
369
+ resolvePtrInvoke(callerMethod, invokeStmt, invokeExpr) {
370
+ const resolveResult = [];
371
+ const ptr = invokeExpr.getFuncPtrLocal();
372
+ this.appendCallbackValueCallSites(ptr, invokeStmt, callerMethod, resolveResult, -1);
127
373
  return resolveResult;
128
374
  }
375
+ /**
376
+ * Resolve a callback value through all supported representations.
377
+ * A value may have both a concrete static target and a field/binding origin;
378
+ * both paths are retained so later field targets cannot be hidden by the
379
+ * concrete FunctionType fast path.
380
+ */
381
+ appendCallbackValueCallSites(value, invokeStmt, callerMethod, resolveResult, argIndex) {
382
+ var _a;
383
+ let functionType = this.unwrapFunctionType(value.getType());
384
+ if (!functionType) {
385
+ if (value instanceof Local_1.Local && this.isCallbackType(value.getType())) {
386
+ const defStmt = value.getDeclaringStmt();
387
+ if (defStmt instanceof Stmt_1.ArkAssignStmt) {
388
+ functionType = this.unwrapFunctionType(defStmt.getRightOp().getType());
389
+ }
390
+ }
391
+ }
392
+ if (functionType) {
393
+ const concreteMethod = this.scene.getMethod(functionType.getMethodSignature());
394
+ if (concreteMethod && concreteMethod.getCfg()) {
395
+ const targetFuncID = this.cg.getCallGraphNodeByMethod(concreteMethod.getSignature()).getID();
396
+ this.appendCallbackCallSite(invokeStmt, callerMethod, targetFuncID, resolveResult, argIndex);
397
+ }
398
+ }
399
+ if (value instanceof Ref_1.ArkInstanceFieldRef) {
400
+ this.subscribeToCallbackField(value, invokeStmt, callerMethod, resolveResult, argIndex);
401
+ }
402
+ const ownerMethod = (_a = invokeStmt.getCfg()) === null || _a === void 0 ? void 0 : _a.getDeclaringMethod();
403
+ if (!(value instanceof Local_1.Local) || !ownerMethod) {
404
+ return;
405
+ }
406
+ const bindingState = this.resolveFuncPtrToBindingState(value, ownerMethod);
407
+ if (bindingState) {
408
+ this.subscribeAndCollectBoundTargets(bindingState, invokeStmt, callerMethod, resolveResult, argIndex);
409
+ }
410
+ const fieldRef = this.resolveFuncPtrToFieldRef(value, ownerMethod);
411
+ if (fieldRef && this.isCallbackType(fieldRef.getType())) {
412
+ this.subscribeToCallbackField(fieldRef, invokeStmt, callerMethod, resolveResult, argIndex);
413
+ }
414
+ }
415
+ appendCallbackCallSite(invokeStmt, callerMethod, targetFuncID, resolveResult, argIndex) {
416
+ const hasTarget = resolveResult.some((callSite) => {
417
+ return callSite.getCalleeFuncID() === targetFuncID;
418
+ });
419
+ if (hasTarget) {
420
+ return;
421
+ }
422
+ resolveResult.push(this.newRTACallSite(invokeStmt, callerMethod, targetFuncID, argIndex));
423
+ }
424
+ subscribeToCallbackField(fieldRef, invokeStmt, callerMethod, resolveResult, argIndex) {
425
+ if (!this.isCallbackType(fieldRef.getType())) {
426
+ return;
427
+ }
428
+ const fieldKey = this.getCallbackFieldKey(fieldRef);
429
+ this.subscribeAndCollectFieldTargets(fieldKey, invokeStmt, callerMethod, resolveResult, argIndex);
430
+ }
431
+ /**
432
+ * Subscribe invokeStmt to bindingState and collect already-known targets as CallSites.
433
+ * Late-arriving targets are delivered via addCallbackTarget → emitCallbackEdge.
434
+ */
435
+ subscribeAndCollectBoundTargets(bindingState, invokeStmt, callerMethod, resolveResult, argIndex) {
436
+ if (bindingState.subscribers.has(invokeStmt)) {
437
+ return;
438
+ }
439
+ bindingState.subscribers.set(invokeStmt, callerMethod);
440
+ for (const targetFuncID of bindingState.targets) {
441
+ this.appendCallbackCallSite(invokeStmt, callerMethod, targetFuncID, resolveResult, argIndex);
442
+ }
443
+ }
444
+ getTypeFromAliasType(aliasType) {
445
+ const visited = this.aliasTypeUnwrapVisited;
446
+ visited.clear();
447
+ let current = aliasType;
448
+ while (current instanceof Type_1.AliasType) {
449
+ if (visited.has(current)) {
450
+ return null;
451
+ }
452
+ visited.add(current);
453
+ current = current.getOriginalType();
454
+ }
455
+ return current;
456
+ }
457
+ /**
458
+ * Whether a type can serve as a callback — FunctionType (direct or aliased)
459
+ * or a ClassType callback wrapper (Callback<T>, AsyncCallback<T>, ...).
460
+ *
461
+ * Unlike {@link unwrapFunctionType}, this accepts ClassType callbacks whose real
462
+ * FunctionType is only discoverable at the assignment site (right-hand side),
463
+ * not from the ClassType itself. This lets the field-subscription and field-store
464
+ * pipelines accept `private cb: Callback<X> = (...) => {...}` fields.
465
+ */
466
+ isCallbackType(type) {
467
+ let targetType = type;
468
+ if (type instanceof Type_1.AliasType) {
469
+ targetType = this.getTypeFromAliasType(type);
470
+ }
471
+ if (targetType instanceof Type_1.FunctionType) {
472
+ return true;
473
+ }
474
+ if (targetType instanceof Type_1.ClassType) {
475
+ const targetSig = targetType.getClassSignature().toString();
476
+ return RapidTypeAnalysis.CALLBACK_CLASS_SIG_KEY.some(sig => targetSig.includes(sig));
477
+ }
478
+ return false;
479
+ }
480
+ /**
481
+ * Peel AliasType layers until a FunctionType is found.
482
+ * Fast-path: FunctionType returns immediately; non-alias types return undefined
483
+ * without touching the visited scratch set.
484
+ */
485
+ unwrapFunctionType(type) {
486
+ let targetType = type;
487
+ if (type instanceof Type_1.AliasType) {
488
+ targetType = this.getTypeFromAliasType(type);
489
+ }
490
+ if (targetType instanceof Type_1.FunctionType) {
491
+ return targetType;
492
+ }
493
+ return null;
494
+ }
495
+ resolveFuncPtrToBindingState(ptr, ownerMethod) {
496
+ const key = this.resolveFuncPtrToBindingKey(ptr, ownerMethod);
497
+ if (key === undefined) {
498
+ return undefined;
499
+ }
500
+ return this.getOrCreateBindingState(this.decodeOwnerFuncID(key), this.decodeLogicalArgIndex(key));
501
+ }
502
+ resolveFuncPtrToBindingKey(ptr, ownerMethod) {
503
+ this.closureTraceVisited.clear();
504
+ const startLocal = this.recoverLocalWithDeclaringStmt(ptr, ownerMethod);
505
+ if (!startLocal) {
506
+ return undefined;
507
+ }
508
+ return this.resolveFuncPtrToBindingKeyRec(startLocal);
509
+ }
510
+ /**
511
+ * Resolve a function-pointer local to an instance callback field.
512
+ * The short-term field model follows one local alias at most; it does not
513
+ * perform receiver points-to analysis.
514
+ */
515
+ resolveFuncPtrToFieldRef(ptr, ownerMethod) {
516
+ this.closureTraceVisited.clear();
517
+ let current = this.recoverLocalWithDeclaringStmt(ptr, ownerMethod);
518
+ while (current) {
519
+ if (this.closureTraceVisited.has(current)) {
520
+ return undefined;
521
+ }
522
+ this.closureTraceVisited.add(current);
523
+ const declaringStmt = current.getDeclaringStmt();
524
+ if (!(declaringStmt instanceof Stmt_1.ArkAssignStmt)) {
525
+ return undefined;
526
+ }
527
+ const rightOp = declaringStmt.getRightOp();
528
+ if (rightOp instanceof Ref_1.ArkInstanceFieldRef) {
529
+ return rightOp;
530
+ }
531
+ if (!(rightOp instanceof Local_1.Local)) {
532
+ return undefined;
533
+ }
534
+ current = this.recoverLocalWithDeclaringStmt(rightOp, ownerMethod);
535
+ }
536
+ return undefined;
537
+ }
538
+ resolveFuncPtrValueToBindingKey(value, ownerMethod) {
539
+ if (value instanceof Local_1.Local) {
540
+ return this.resolveFuncPtrToBindingKey(value, ownerMethod);
541
+ }
542
+ if (!(value instanceof Ref_1.ArkParameterRef)) {
543
+ return undefined;
544
+ }
545
+ const logicalArgIndex = this.toLogicalArgIndex(ownerMethod, value.getIndex());
546
+ if (logicalArgIndex < 0) {
547
+ return undefined;
548
+ }
549
+ const ownerFuncID = this.cg.getCallGraphNodeByMethod(ownerMethod.getSignature()).getID();
550
+ return this.encodeCallbackBindingKey(ownerFuncID, logicalArgIndex);
551
+ }
552
+ /**
553
+ * AliasType-typed ptr locals on invoke exprs may lack declaringStmt even when the
554
+ * method body has a same-named local that carries the real definition.
555
+ */
556
+ recoverLocalWithDeclaringStmt(local, ownerMethod) {
557
+ if (local.getDeclaringStmt()) {
558
+ return local;
559
+ }
560
+ const body = ownerMethod.getBody();
561
+ if (!body) {
562
+ return undefined;
563
+ }
564
+ const candidate = body.getLocals().get(local.getName());
565
+ if (candidate && candidate.getDeclaringStmt()) {
566
+ return candidate;
567
+ }
568
+ return undefined;
569
+ }
570
+ /**
571
+ * Recursively trace a Local back to a parameter binding key.
572
+ * Follows local aliases and unique closure captures until an ArkParameterRef is
573
+ * found; returns the packed (ownerFuncID, logicalArgIndex) key, or undefined if
574
+ * the value does not originate from a parameter slot.
575
+ */
576
+ resolveFuncPtrToBindingKeyRec(local) {
577
+ var _a;
578
+ if (this.closureTraceVisited.has(local)) {
579
+ return undefined;
580
+ }
581
+ this.closureTraceVisited.add(local);
582
+ const declaringStmt = local.getDeclaringStmt();
583
+ if (!(declaringStmt instanceof Stmt_1.ArkAssignStmt)) {
584
+ return undefined;
585
+ }
586
+ const rightOp = declaringStmt.getRightOp();
587
+ if (rightOp instanceof Ref_1.ArkParameterRef) {
588
+ const ownerMethod = (_a = declaringStmt.getCfg()) === null || _a === void 0 ? void 0 : _a.getDeclaringMethod();
589
+ if (!ownerMethod) {
590
+ return undefined;
591
+ }
592
+ const logicalArgIndex = this.toLogicalArgIndex(ownerMethod, rightOp.getIndex());
593
+ if (logicalArgIndex < 0) {
594
+ return undefined;
595
+ }
596
+ const ownerFuncID = this.cg.getCallGraphNodeByMethod(ownerMethod.getSignature()).getID();
597
+ return this.encodeCallbackBindingKey(ownerFuncID, logicalArgIndex);
598
+ }
599
+ // Local alias: const invoke = cb;
600
+ if (rightOp instanceof Local_1.Local) {
601
+ return this.resolveFuncPtrToBindingKeyRec(rightOp);
602
+ }
603
+ if (rightOp instanceof Ref_1.ClosureFieldRef) {
604
+ const baseType = rightOp.getBase().getType();
605
+ if (!(baseType instanceof Type_1.LexicalEnvType)) {
606
+ return undefined;
607
+ }
608
+ const closures = baseType.getClosures();
609
+ const fieldName = rightOp.getFieldName();
610
+ let match;
611
+ let matchCount = 0;
612
+ for (let i = 0; i < closures.length; i++) {
613
+ if (closures[i].getName() === fieldName) {
614
+ match = closures[i];
615
+ matchCount++;
616
+ }
617
+ }
618
+ if (matchCount !== 1 || !match) {
619
+ return undefined;
620
+ }
621
+ return this.resolveFuncPtrToBindingKeyRec(match);
622
+ }
623
+ return undefined;
624
+ }
625
+ toLogicalArgIndex(method, rawParamIndex) {
626
+ const params = method.getParameters();
627
+ let offset = 0;
628
+ if (params && params.length > 0 && params[0].getType() instanceof Type_1.LexicalEnvType) {
629
+ offset = 1;
630
+ }
631
+ const logicalIndex = rawParamIndex - offset;
632
+ if (logicalIndex < 0) {
633
+ return -1;
634
+ }
635
+ return logicalIndex;
636
+ }
637
+ encodeCallbackBindingKey(ownerFuncID, logicalArgIndex) {
638
+ return ownerFuncID * CALLBACK_BINDING_ARG_STRIDE + logicalArgIndex;
639
+ }
640
+ decodeOwnerFuncID(key) {
641
+ return Math.floor(key / CALLBACK_BINDING_ARG_STRIDE);
642
+ }
643
+ decodeLogicalArgIndex(key) {
644
+ return key % CALLBACK_BINDING_ARG_STRIDE;
645
+ }
646
+ getOrCreateBindingState(ownerFuncID, logicalArgIndex) {
647
+ let states = this.callbackBindings.get(ownerFuncID);
648
+ if (!states) {
649
+ states = [];
650
+ this.callbackBindings.set(ownerFuncID, states);
651
+ }
652
+ while (states.length <= logicalArgIndex) {
653
+ states.push(undefined);
654
+ }
655
+ let state = states[logicalArgIndex];
656
+ if (!state) {
657
+ state = {
658
+ targets: new Set(),
659
+ subscribers: new Map(),
660
+ forwards: new Set(),
661
+ fieldForwards: new Set(),
662
+ };
663
+ states[logicalArgIndex] = state;
664
+ }
665
+ return state;
666
+ }
667
+ getCallbackFieldKey(fieldRef) {
668
+ return fieldRef.getFieldSignature().toString();
669
+ }
670
+ getOrCreateCallbackFieldState(fieldKey) {
671
+ let state = this.callbackFieldBindings.get(fieldKey);
672
+ if (!state) {
673
+ state = {
674
+ targets: new Set(),
675
+ subscribers: new Map(),
676
+ bindingForwards: new Set(),
677
+ };
678
+ this.callbackFieldBindings.set(fieldKey, state);
679
+ }
680
+ return state;
681
+ }
682
+ linkCallbackField(source, fieldKey) {
683
+ const sourceState = this.getOrCreateBindingState(this.decodeOwnerFuncID(source), this.decodeLogicalArgIndex(source));
684
+ if (sourceState.fieldForwards.has(fieldKey)) {
685
+ return;
686
+ }
687
+ sourceState.fieldForwards.add(fieldKey);
688
+ for (const targetFuncID of sourceState.targets) {
689
+ this.addCallbackFieldTarget(fieldKey, targetFuncID);
690
+ }
691
+ }
692
+ addCallbackFieldTarget(fieldKey, targetFuncID) {
693
+ const state = this.getOrCreateCallbackFieldState(fieldKey);
694
+ if (state.targets.has(targetFuncID)) {
695
+ return;
696
+ }
697
+ state.targets.add(targetFuncID);
698
+ for (const [stmt, subscriberList] of state.subscribers) {
699
+ for (const sub of subscriberList) {
700
+ this.emitCallbackEdge(sub.callerFuncID, targetFuncID, stmt, sub.argIndex);
701
+ }
702
+ }
703
+ for (const bindingKey of state.bindingForwards) {
704
+ this.addCallbackTarget(this.decodeOwnerFuncID(bindingKey), this.decodeLogicalArgIndex(bindingKey), targetFuncID);
705
+ }
706
+ }
707
+ /**
708
+ * Link a callback field to a callee parameter binding slot. When the field's
709
+ * targets are populated later (e.g. field initializer processed after the
710
+ * call site due to RTA workList ordering), targets propagate to the binding
711
+ * slot via {@link addCallbackFieldTarget} → bindingForwards → addCallbackTarget.
712
+ */
713
+ linkFieldToBinding(fieldKey, bindingKey) {
714
+ const fieldState = this.getOrCreateCallbackFieldState(fieldKey);
715
+ if (fieldState.bindingForwards.has(bindingKey)) {
716
+ return;
717
+ }
718
+ fieldState.bindingForwards.add(bindingKey);
719
+ for (const targetFuncID of fieldState.targets) {
720
+ this.addCallbackTarget(this.decodeOwnerFuncID(bindingKey), this.decodeLogicalArgIndex(bindingKey), targetFuncID);
721
+ }
722
+ }
723
+ subscribeAndCollectFieldTargets(fieldKey, invokeStmt, callerMethod, resolveResult, argIndex) {
724
+ const state = this.getOrCreateCallbackFieldState(fieldKey);
725
+ if (!state.subscribers.has(invokeStmt)) {
726
+ state.subscribers.set(invokeStmt, []);
727
+ }
728
+ const list = state.subscribers.get(invokeStmt);
729
+ if (list.some(s => s.callerFuncID === callerMethod && s.argIndex === argIndex)) {
730
+ return;
731
+ }
732
+ list.push({ callerFuncID: callerMethod, argIndex });
733
+ for (const targetFuncID of state.targets) {
734
+ this.appendCallbackCallSite(invokeStmt, callerMethod, targetFuncID, resolveResult, argIndex);
735
+ }
736
+ }
737
+ recordCallbackFieldStore(stmt, ownerMethod) {
738
+ const leftOp = stmt.getLeftOp();
739
+ if (!(leftOp instanceof Ref_1.ArkInstanceFieldRef)) {
740
+ return;
741
+ }
742
+ if (!this.isCallbackType(leftOp.getType())) {
743
+ return;
744
+ }
745
+ const fieldKey = this.getCallbackFieldKey(leftOp);
746
+ const rightOp = stmt.getRightOp();
747
+ const functionType = this.unwrapFunctionType(rightOp.getType());
748
+ if (functionType) {
749
+ const callbackMethod = this.scene.getMethod(functionType.getMethodSignature());
750
+ if (callbackMethod && callbackMethod.getCfg()) {
751
+ const targetFuncID = this.cg.getCallGraphNodeByMethod(callbackMethod.getSignature()).getID();
752
+ this.addCallbackFieldTarget(fieldKey, targetFuncID);
753
+ }
754
+ }
755
+ const sourceKey = this.resolveFuncPtrValueToBindingKey(rightOp, ownerMethod);
756
+ if (sourceKey !== undefined) {
757
+ this.linkCallbackField(sourceKey, fieldKey);
758
+ }
759
+ }
760
+ /**
761
+ * Link source parameter binding to a callee parameter slot and copy existing targets.
762
+ * Future addCallbackTarget calls on the source also propagate through forwards.
763
+ */
764
+ linkCallbackBinding(source, target) {
765
+ if (source === target) {
766
+ return;
767
+ }
768
+ const sourceState = this.getOrCreateBindingState(this.decodeOwnerFuncID(source), this.decodeLogicalArgIndex(source));
769
+ sourceState.forwards.add(target);
770
+ const targetOwnerFuncID = this.decodeOwnerFuncID(target);
771
+ const targetLogicalArgIndex = this.decodeLogicalArgIndex(target);
772
+ for (const targetFuncID of sourceState.targets) {
773
+ this.addCallbackTarget(targetOwnerFuncID, targetLogicalArgIndex, targetFuncID);
774
+ }
775
+ }
776
+ addCallbackTarget(ownerFuncID, logicalArgIndex, targetFuncID) {
777
+ const state = this.getOrCreateBindingState(ownerFuncID, logicalArgIndex);
778
+ if (state.targets.has(targetFuncID)) {
779
+ return;
780
+ }
781
+ state.targets.add(targetFuncID);
782
+ for (const [stmt, callerFuncID] of state.subscribers) {
783
+ this.emitCallbackEdge(callerFuncID, targetFuncID, stmt, logicalArgIndex);
784
+ }
785
+ for (const forward of state.forwards) {
786
+ this.addCallbackTarget(this.decodeOwnerFuncID(forward), this.decodeLogicalArgIndex(forward), targetFuncID);
787
+ }
788
+ for (const fieldKey of state.fieldForwards) {
789
+ this.addCallbackFieldTarget(fieldKey, targetFuncID);
790
+ }
791
+ }
792
+ /**
793
+ * Create a CallbackCallSite, register it via CallSiteManager (for ID allocation
794
+ * and getCallSiteById lookup) and via CallGraph maps (for getCallSiteByStmt and
795
+ * getCallSitesByMethod lookup). The argIndex is set for callback virtual edges
796
+ * and left undefined for regular (direct/virtual/constructor) call sites.
797
+ */
798
+ newRTACallSite(invokeStmt, callerFuncID, calleeFuncID, argIndex) {
799
+ const base = this.cg.getCallSiteManager().newCallSite(invokeStmt, undefined, calleeFuncID, callerFuncID);
800
+ const cs = new CallGraph_1.CallSite(base.id, base.callStmt, base.args, base.calleeFuncID, base.callerFuncID, argIndex);
801
+ this.cg.recordCallSite(invokeStmt, cs);
802
+ return cs;
803
+ }
804
+ emitCallbackEdge(callerFuncID, calleeFuncID, callStmt, argIndex) {
805
+ this.cg.addDynamicCallEdge(callerFuncID, calleeFuncID, callStmt);
806
+ this.newRTACallSite(callStmt, callerFuncID, calleeFuncID, argIndex);
807
+ const calleeNode = this.cg.getNode(calleeFuncID);
808
+ if (!calleeNode || calleeNode.isSdkMethod()) {
809
+ return;
810
+ }
811
+ if (this.processedMethod.contains(calleeFuncID)) {
812
+ return;
813
+ }
814
+ this.workList.push(calleeFuncID);
815
+ }
129
816
  getOrBuildVirtualDispatchRows(declareClass, methodName) {
130
817
  const declareSig = declareClass.getSignature();
131
818
  let methodIndex = this.virtualDispatchIndex.get(declareSig);
@@ -140,7 +827,7 @@ class RapidTypeAnalysis extends AbstractAnalysis_1.AbstractAnalysis {
140
827
  const rows = {
141
828
  classSignatures: [],
142
829
  calleeNodeIDs: [],
143
- isSdkClasses: [],
830
+ isUnconditionallyReachable: [],
144
831
  };
145
832
  for (const arkClass of this.getClassHierarchy(declareClass)) {
146
833
  const possibleCalleeMethod = arkClass.getMethodWithName(methodName);
@@ -148,16 +835,19 @@ class RapidTypeAnalysis extends AbstractAnalysis_1.AbstractAnalysis {
148
835
  continue;
149
836
  }
150
837
  const classSignature = arkClass.getSignature();
151
- if (possibleCalleeMethod.isGenerated() &&
152
- classSignature !== declareSig) {
838
+ if (possibleCalleeMethod.isGenerated() && classSignature !== declareSig) {
153
839
  continue;
154
840
  }
155
841
  if (possibleCalleeMethod.isAbstract()) {
156
842
  continue;
157
843
  }
844
+ const calleeNodeID = this.cg.getCallGraphNodeByMethod(possibleCalleeMethod.getSignature()).getID();
158
845
  rows.classSignatures.push(classSignature);
159
- rows.calleeNodeIDs.push(this.cg.getCallGraphNodeByMethod(possibleCalleeMethod.getSignature()).getID());
160
- rows.isSdkClasses.push(this.scene.hasSdkFile(classSignature.getDeclaringFileSignature()));
846
+ rows.calleeNodeIDs.push(calleeNodeID);
847
+ // OH SDK declare targets, and invoking built-ins (Promise.then / Array.forEach /
848
+ // ...), need no local `new` to be callable. Container non-invoking APIs still require
849
+ // instancedClasses (e.g. newarray → Array) so push/indexOf/set are not always-on.
850
+ rows.isUnconditionallyReachable.push(classSignature === declareSig && this.shouldSynthesizeVirtualCallback(calleeNodeID));
161
851
  }
162
852
  methodIndex.set(methodName, rows);
163
853
  return rows;
@@ -177,7 +867,7 @@ class RapidTypeAnalysis extends AbstractAnalysis_1.AbstractAnalysis {
177
867
  while (curClass) {
178
868
  const methodInClass = curClass.getMethodWithName(methodName);
179
869
  if (methodInClass && !methodInClass.isAbstract()) {
180
- return this.cg.getCallSiteManager().newCallSite(invokeStmt, undefined, this.cg.getCallGraphNodeByMethod(methodInClass.getSignature()).getID(), callerMethod);
870
+ return this.newRTACallSite(invokeStmt, callerMethod, this.cg.getCallGraphNodeByMethod(methodInClass.getSignature()).getID());
181
871
  }
182
872
  curClass = curClass.getSuperClass();
183
873
  }
@@ -208,25 +898,20 @@ class RapidTypeAnalysis extends AbstractAnalysis_1.AbstractAnalysis {
208
898
  if (!constructorMethod) {
209
899
  return null;
210
900
  }
211
- return this.cg.getCallSiteManager().newCallSite(invokeStmt, undefined, this.cg.getCallGraphNodeByMethod(constructorMethod.getSignature()).getID(), callerMethod);
901
+ return this.newRTACallSite(invokeStmt, callerMethod, this.cg.getCallGraphNodeByMethod(constructorMethod.getSignature()).getID());
212
902
  }
213
903
  preProcessMethod(funcID) {
214
- let newCallSites = [];
215
- let instancedClasses = this.collectInstancedClassesInMethod(funcID);
216
- let newlyInstancedClasses = new Set();
217
- for (const sig of instancedClasses) {
218
- if (!this.instancedClasses.has(sig)) {
219
- newlyInstancedClasses.add(sig);
904
+ const newCallSites = [];
905
+ const instancedInMethod = this.collectInstancedClassesInMethod(funcID);
906
+ instancedInMethod.forEach((sig) => {
907
+ if (this.instancedClasses.has(sig)) {
908
+ return;
220
909
  }
221
- }
222
- newlyInstancedClasses.forEach(sig => {
223
- let ignoredCalls = this.ignoredCalls.get(sig);
910
+ const ignoredCalls = this.ignoredCalls.get(sig);
224
911
  if (ignoredCalls) {
225
- ignoredCalls.forEach(call => {
912
+ ignoredCalls.forEach((call) => {
226
913
  this.cg.addDynamicCallEdge(call.caller, call.callee, call.callStmt);
227
- const newCallSite = this.cg.getCallSiteManager().newCallSite(call.callStmt, undefined, call.callee, call.caller);
228
- this.cg.addStmtToCallSiteMap(call.callStmt, newCallSite);
229
- this.cg.addMethodToCallSiteMap(call.callee, newCallSite);
914
+ const newCallSite = this.newRTACallSite(call.callStmt, call.caller, call.callee);
230
915
  newCallSites.push(newCallSite);
231
916
  });
232
917
  }
@@ -247,26 +932,67 @@ class RapidTypeAnalysis extends AbstractAnalysis_1.AbstractAnalysis {
247
932
  return instancedClasses;
248
933
  }
249
934
  for (let stmt of cfg.getStmts()) {
250
- let stmtExpr;
251
- if (stmt instanceof Stmt_1.ArkAssignStmt && stmt.getRightOp() instanceof Expr_1.ArkNewExpr) {
252
- stmtExpr = stmt.getRightOp();
935
+ if (!(stmt instanceof Stmt_1.ArkAssignStmt)) {
936
+ continue;
253
937
  }
254
- else {
938
+ const rightOp = stmt.getRightOp();
939
+ this.recordCallbackFieldStore(stmt, arkMethod);
940
+ if (rightOp instanceof Expr_1.ArkNewExpr) {
941
+ const classSig = rightOp.getType().getClassSignature();
942
+ if (classSig !== null && classSig !== undefined) {
943
+ // TODO: need to check if different stmt has single sig
944
+ instancedClasses.add(classSig);
945
+ }
255
946
  continue;
256
947
  }
257
- let classSig = stmtExpr.getType().getClassSignature();
258
- if (classSig != null) {
259
- // TODO: need to check if different stmt has single sig
260
- instancedClasses.add(classSig);
948
+ // `handlers = []` lowers to newarray, not `new Array`. Treat it as Array instantiation
949
+ // so built-in Array methods (e.g. push) can pass the RTA instancedClasses gate.
950
+ if (rightOp instanceof Expr_1.ArkNewArrayExpr) {
951
+ const arraySig = this.getBuiltinArrayClassSignature();
952
+ if (arraySig) {
953
+ instancedClasses.add(arraySig);
954
+ }
261
955
  }
262
956
  }
263
957
  return instancedClasses;
264
958
  }
959
+ getBuiltinArrayClassSignature() {
960
+ if (this.builtinArrayClassResolved) {
961
+ return this.builtinArrayClassSignature;
962
+ }
963
+ this.builtinArrayClassResolved = true;
964
+ // Built-in Array is not in scene.getClasses(); use the SDK global export
965
+ // (lib.es5 Array) so the signature matches virtual-dispatch declareClass.
966
+ const globalArray = this.scene.getSdkGlobal(BUILTIN_ARRAY_CLASS_NAME);
967
+ if (globalArray instanceof ArkClass_1.ArkClass) {
968
+ const classSig = globalArray.getSignature();
969
+ if (classSig.getDeclaringFileSignature().getProjectName() === SdkUtils_1.SdkUtils.BUILT_IN_NAME) {
970
+ this.builtinArrayClassSignature = classSig;
971
+ }
972
+ }
973
+ return this.builtinArrayClassSignature;
974
+ }
265
975
  addIgnoredCalls(arkClass, callerID, calleeID, invokeStmt) {
266
- var _a;
267
- let classMap = (_a = this.ignoredCalls.get(arkClass)) !== null && _a !== void 0 ? _a : new Set();
976
+ let classMap = this.ignoredCalls.get(arkClass);
977
+ if (!classMap) {
978
+ classMap = new Set();
979
+ this.ignoredCalls.set(arkClass, classMap);
980
+ }
981
+ for (const existing of classMap) {
982
+ if (existing.caller === callerID &&
983
+ existing.callee === calleeID &&
984
+ existing.callStmt === invokeStmt) {
985
+ return;
986
+ }
987
+ }
268
988
  classMap.add({ caller: callerID, callee: calleeID, callStmt: invokeStmt });
269
- this.ignoredCalls.set(arkClass, classMap);
270
989
  }
271
990
  }
272
991
  exports.RapidTypeAnalysis = RapidTypeAnalysis;
992
+ RapidTypeAnalysis.CALLBACK_CLASS_SIG_KEY = [
993
+ '@ohos.base.d.ts: Callback', '@ohos.window.d.ts: Callback', 'component/common.d.ts: Callback',
994
+ '@ohos.base.d.ts: ErrorCallback',
995
+ '@ohos.base.d.ts: AsyncCallback',
996
+ '@ohos.base.d.ts: BusinessError',
997
+ 'lib.es5.d.ts: Function',
998
+ ];