Project

General

Profile

11808-20260831a.diff

Hynek Cihlar, 08/31/2026 02:27 PM

Download (35.2 KB)

View differences:

new/rules/annotations/com_origin.rules 2026-08-31 17:04:33 +0000
1
<?xml version="1.0"?>
2

  
3
<!--
4
/*
5
** Module   : com_origin.rules
6
** Abstract : associates every COM/OCX referent with the control or object it originated from
7
**
8
** Copyright (c) 2026, Golden Code Development Corporation.
9
**
10
** _#_ _I_ __Date__ _______________________________________Description_______________________________________
11
** 001 HC  20260831 Created initial version. Annotates each COM-HANDLE declaration with the identity of the
12
**                  control or automation object it was loaded from or assigned from, so that the analytics
13
**                  reports can attribute every method and property access to its originating control.
14
**                  Refs #11808.
15
*/
16
 -->
17

  
18
<!--
19
** This program is free software: you can redistribute it and/or modify
20
** it under the terms of the GNU Affero General Public License as
21
** published by the Free Software Foundation, either version 3 of the
22
** License, or (at your option) any later version.
23
**
24
** This program is distributed in the hope that it will be useful,
25
** but WITHOUT ANY WARRANTY; without even the implied warranty of
26
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27
** GNU Affero General Public License for more details.
28
**
29
** You may find a copy of the GNU Affero GPL version 3 at the following
30
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
31
**
32
** Additional terms under GNU Affero GPL version 3 section 7:
33
**
34
**   Under Section 7 of the GNU Affero GPL version 3, the following additional
35
**   terms apply to the works covered under the License.  These additional terms
36
**   are non-permissive additional terms allowed under Section 7 of the GNU
37
**   Affero GPL version 3 and may not be removed by you.
38
**
39
**   0. Attribution Requirement.
40
**
41
**     You must preserve all legal notices or author attributions in the covered
42
**     work or Appropriate Legal Notices displayed by works containing the covered
43
**     work.  You may not remove from the covered work any author or developer
44
**     credit already included within the covered work.
45
**
46
**   1. No License To Use Trademarks.
47
**
48
**     This license does not grant any license or rights to use the trademarks
49
**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
50
**     of Golden Code Development Corporation. You are not authorized to use the
51
**     name Golden Code, FWD, or the names of any author or contributor, for
52
**     publicity purposes without written authorization.
53
**
54
**   2. No Misrepresentation of Affiliation.
55
**
56
**     You may not represent yourself as Golden Code Development Corporation or FWD.
57
**
58
**     You may not represent yourself for publicity purposes as associated with
59
**     Golden Code Development Corporation, FWD, or any author or contributor to
60
**     the covered work, without written authorization.
61
**
62
**   3. No Misrepresentation of Source or Origin.
63
**
64
**     You may not represent the covered work as solely your work.  All modified
65
**     versions of the covered work must be marked in a reasonable way to make it
66
**     clear that the modified work is not originating from Golden Code Development
67
**     Corporation or FWD.  All modified versions must contain the notices of
68
**     attribution required in this license.
69
-->
70

  
71
<!--
72
** Every COM or OCX method/property access in 4GL is made through a COM-HANDLE referent. The
73
** referent alone does not say WHICH control or automation object is being driven; that is
74
** established elsewhere, at the point the handle was created, loaded or assigned:
75
**
76
**   CREATE "Excel.Application" chExcel.            COM automation, identity is the ProgID
77
**   chFrame = CtrlFrame:COM-HANDLE.                OCX, identity begins with the control frame
78
**   chFrame:LoadControls("x.wrx", "Section").      OCX, identity gains the .wrx file and section
79
**   chCtl = chFrame:ListView.                      OCX, identity gains the named control
80
**   chBook = chExcel:Workbooks:Add().              identity is inherited from the chain root
81
**
82
** This rule set records that identity on the DECLARATION node of each COM-HANDLE (the node that
83
** every reference points at through its "refid" annotation), under two annotations:
84
**
85
**   com-origin        the control/object identity string
86
**   com-origin-kind   com-automation | ocx-control | ocx-control-frame | com-derived
87
**
88
** A declaration that carries no com-origin annotation after this pass could not be resolved
89
** statically (the handle arrives as a parameter, through a shared variable, from a computed
90
** ProgID, or from another compilation unit). That is a legitimate and expected outcome; the
91
** reports treat a missing annotation as an unresolved origin and count it separately rather
92
** than dropping the access.
93
**
94
** This runs as part of the F2 front end (annotations/early_annotations), NOT the CB back end,
95
** because the analytics pipeline only ever executes the front end. The annotations are persisted
96
** into the .ast artifacts by BinaryAstCodec and are read back, read-only, by ReportDriver in a
97
** separate JVM. Since the report is a later pass over persisted trees, the annotation only has
98
** to be correct by the end of this file's walk: origins are therefore written as soon as they
99
** are discovered and refined in place as more becomes known, and no deferred application pass
100
** is needed.
101
-->
102

  
103
<rule-set input="tree">
104

  
105
   <!-- register worker objects -->
106
   <worker class="com.goldencode.p2j.uast.ProgressPatternWorker"         namespace="prog" />
107
   <worker class="com.goldencode.p2j.convert.ExpressionConversionWorker" namespace="ecw" />
108

  
109
   <!-- node scratch -->
110
   <variable name="href"     type="com.goldencode.ast.Aast" />
111
   <variable name="enode"    type="com.goldencode.ast.Aast" />
112
   <variable name="lref"     type="com.goldencode.ast.Aast" />
113
   <variable name="rref"     type="com.goldencode.ast.Aast" />
114
   <variable name="cmeth"    type="com.goldencode.ast.Aast" />
115
   <variable name="pnode"    type="com.goldencode.ast.Aast" />
116

  
117
   <!-- value scratch -->
118
   <variable name="declId"   type="java.lang.Long" />
119
   <variable name="srcId"    type="java.lang.Long" />
120
   <variable name="ident"    type="java.lang.String" />
121
   <variable name="skind"    type="java.lang.String" />
122
   <variable name="frame"    type="java.lang.String" />
123
   <variable name="wrx"      type="java.lang.String" />
124
   <variable name="sect"     type="java.lang.String" />
125

  
126
   <!-- per-file accumulated knowledge, keyed by declaration node id -->
127
   <variable name="originOf" type="java.util.Map" />
128
   <variable name="kindOf"   type="java.util.Map" />
129
   <variable name="frameOf"  type="java.util.Map" />
130
   <variable name="wrxOf"    type="java.util.Map" />
131
   <variable name="sectOf"   type="java.util.Map" />
132

  
133
   <!-- expression libraries -->
134
   <include name="common-progress" />
135

  
136
   <func-library access="private">
137

  
138
      <!-- True only for a genuine COM-HANDLE typed node. The shared com_type() library function
139
           also accepts plain widget HANDLE types, which is too broad here: a widget handle is
140
           not a COM referent and must not be given a com-origin. -->
141
      <function name="com_handle_type">
142
         <parameter name="ttype" type="java.lang.Integer" />
143
         ttype == prog.var_com_handle  or ttype == prog.field_com_handle   or
144
         ttype == prog.func_com_handle or ttype == prog.attr_com_handle    or
145
         ttype == prog.meth_com_handle or ttype == prog.oo_meth_com_handle
146
      </function>
147

  
148
      <!-- The declaration node id a reference resolves to, or the node's own id when it IS the
149
           declaration. Returns null when neither is available. -->
150
      <function name="decl_id">
151
         <parameter name="target" type="com.goldencode.ast.Aast" />
152
         <return    name="rid"    type="java.lang.Long" />
153

  
154
         <rule>rid = null</rule>
155

  
156
         <rule>target != null
157
            <rule>target.isAnnotation("refid")
158
               <action>rid = #(java.lang.Long) target.getAnnotation("refid")</action>
159
               <action on="false">rid = target.getId()</action>
160
            </rule>
161
         </rule>
162
      </function>
163

  
164
      <!-- Descend the left edge of a COM invocation/attribute chain to the referent it is rooted
165
           at. For chExcel:Workbooks:Add() this yields the chExcel reference. -->
166
      <function name="com_root_ref">
167
         <parameter name="target" type="com.goldencode.ast.Aast" />
168
         <return    name="ref"    type="com.goldencode.ast.Aast" />
169

  
170
         <rule>ref = target</rule>
171

  
172
         <while>ref != null                            and
173
                (ref.type == prog.com_invocation  or
174
                 ref.type == prog.colon)
175
            <action>ref = ref.getChildAt(0)</action>
176
         </while>
177
      </function>
178

  
179
      <!-- Literal text of a string node, unquoted and normalized, or null when the node is not a
180
           string literal (a computed expression, a variable, a function call). -->
181
      <function name="literal_text">
182
         <parameter name="target" type="com.goldencode.ast.Aast" />
183
         <return    name="txt"    type="java.lang.String" />
184

  
185
         <rule>txt = null</rule>
186

  
187
         <rule>target != null and target.type == prog.string
188
            <action>txt = ecw.progressToJavaString(target.text)</action>
189
         </rule>
190
      </function>
191

  
192
      <!-- The single expression payload under an EXPRESSION wrapper (or the node itself when it
193
           is not wrapped). -->
194
      <function name="expr_payload">
195
         <parameter name="target" type="com.goldencode.ast.Aast" />
196
         <return    name="ref"    type="com.goldencode.ast.Aast" />
197

  
198
         <rule>ref = target</rule>
199

  
200
         <while>ref != null and ref.type == prog.expression
201
            <action>ref = ref.getChildAt(0)</action>
202
         </while>
203
      </function>
204

  
205
      <!-- Compose an OCX control identity from the pieces known so far. The .wrx file name is
206
           frequently passed to LoadControls() as a variable rather than a literal, so the
207
           identity degrades gracefully: wrx#section/control, section/control, or frame. -->
208
      <function name="compose_ocx">
209
         <parameter name="fname" type="java.lang.String" />
210
         <parameter name="wname" type="java.lang.String" />
211
         <parameter name="sname" type="java.lang.String" />
212
         <parameter name="cname" type="java.lang.String" />
213
         <return    name="txt"   type="java.lang.String" />
214

  
215
         <!-- least specific fallback: the control frame's own name -->
216
         <rule>txt = fname</rule>
217

  
218
         <rule>sname != null
219
            <action>txt = sname</action>
220

  
221
            <rule>wname != null
222
               <action>txt = sprintf("%s#%s", wname, sname)</action>
223
            </rule>
224
         </rule>
225

  
226
         <rule>cname != null
227
            <action>txt = sprintf("%s/%s", txt, cname)</action>
228
         </rule>
229
      </function>
230

  
231
   </func-library>
232

  
233
   <init-rules>
234
      <rule>originOf = create("java.util.HashMap")</rule>
235
      <rule>kindOf   = create("java.util.HashMap")</rule>
236
      <rule>frameOf  = create("java.util.HashMap")</rule>
237
      <rule>wrxOf    = create("java.util.HashMap")</rule>
238
      <rule>sectOf   = create("java.util.HashMap")</rule>
239
   </init-rules>
240

  
241
   <walk-rules>
242

  
243
      <!-- ================================================================================
244
           COM automation object creation:  CREATE "Excel.Application" chExcel.
245
           Matched on the COM-HANDLE operand rather than on the CREATE_OBJECT node itself, so
246
           that no assumption is made about the order of the statement's children.
247
           ================================================================================ -->
248
      <rule>evalLib("com_handle_type", type)      and
249
            parent.type == prog.create_object
250

  
251
         <action>declId = execLib("decl_id", this)</action>
252
         <action>enode  = parent.getImmediateChild(prog.expression, null)</action>
253
         <action>ident  = execLib("literal_text", execLib("expr_payload", enode))</action>
254

  
255
         <rule>declId != null
256

  
257
            <!-- A non-literal ProgID (CREATE VALUE(expr) h, or a variable) cannot be resolved
258
                 statically. Leave the declaration unannotated so it is counted as unresolved
259
                 rather than attributed to the wrong object. -->
260
            <rule>ident != null
261
               <action>originOf.put(declId, ident)</action>
262
               <action>kindOf.put(declId, "com-automation")</action>
263
               <action>putReferenceNote(declId, "com-origin", ident)</action>
264
               <action>putReferenceNote(declId, "com-origin-kind", "com-automation")</action>
265
            </rule>
266
         </rule>
267
      </rule>
268

  
269
      <!-- ================================================================================
270
           Control frame binding:  chFrame = CtrlFrame:COM-HANDLE.
271
           The COM-HANDLE attribute node's parent chain gives the control frame widget on the
272
           left; the enclosing assignment gives the COM-HANDLE variable being bound to it.
273
           ================================================================================ -->
274
      <rule>type == prog.attr_com_handle             and
275
            parent != null                           and
276
            (parent.type == prog.colon            or
277
             parent.type == prog.com_invocation)
278

  
279
         <action>href  = parent.getChildAt(0)</action>
280
         <action>frame = null</action>
281

  
282
         <rule>href != null
283
            <action>frame = href.text</action>
284
         </rule>
285

  
286
         <!-- locate the assignment target holding this expression -->
287
         <action>pnode = parent.parent</action>
288
         <action>lref  = null</action>
289

  
290
         <rule>pnode != null                            and
291
               (pnode.type == prog.assignment       or
292
                pnode.type == prog.assign)
293
            <action>lref = pnode.getChildAt(0)</action>
294
         </rule>
295

  
296
         <rule>lref != null and frame != null and evalLib("com_handle_type", lref.type)
297
            <action>declId = execLib("decl_id", lref)</action>
298

  
299
            <rule>declId != null
300
               <action>frameOf.put(declId, frame)</action>
301
               <action>ident = execLib("compose_ocx",
302
                                       frame,
303
                                       #(java.lang.String) wrxOf.get(declId),
304
                                       #(java.lang.String) sectOf.get(declId),
305
                                       null)</action>
306
               <action>originOf.put(declId, ident)</action>
307
               <action>kindOf.put(declId, "ocx-control-frame")</action>
308
               <action>putReferenceNote(declId, "com-origin", ident)</action>
309
               <action>putReferenceNote(declId, "com-origin-kind", "ocx-control-frame")</action>
310
            </rule>
311
         </rule>
312
      </rule>
313

  
314
      <!-- ================================================================================
315
           Control loading:  chFrame:LoadControls("controls.wrx", "SectionName").
316
           ocx_control_load() matches the second argument of the call, which is the .wrx section
317
           name. The first argument is the .wrx file, commonly a variable rather than a literal.
318
           ================================================================================ -->
319
      <rule>evalLib("ocx_control_load", this)
320

  
321
         <action>cmeth = parent.parent</action>
322
         <action>sect  = execLib("literal_text", this)</action>
323
         <action>wrx   = null</action>
324
         <action>href  = null</action>
325

  
326
         <rule>cmeth != null
327
            <!-- first argument: the .wrx file -->
328
            <action>pnode = cmeth.getChildAt(0)</action>
329

  
330
            <rule>pnode != null
331
               <action>wrx = execLib("literal_text", execLib("expr_payload", pnode.getChildAt(0)))</action>
332
            </rule>
333

  
334
            <!-- the referent the call was made on: the control frame's COM-HANDLE -->
335
            <rule>cmeth.parent != null and cmeth.parent.type == prog.com_invocation
336
               <action>href = execLib("com_root_ref", cmeth.parent.getChildAt(0))</action>
337
            </rule>
338
         </rule>
339

  
340
         <rule>href != null and evalLib("com_handle_type", href.type)
341
            <action>declId = execLib("decl_id", href)</action>
342

  
343
            <rule>declId != null
344
               <rule>wrx != null
345
                  <action>wrxOf.put(declId, wrx)</action>
346
               </rule>
347
               <rule>sect != null
348
                  <action>sectOf.put(declId, sect)</action>
349
               </rule>
350

  
351
               <action>ident = execLib("compose_ocx",
352
                                       #(java.lang.String) frameOf.get(declId),
353
                                       #(java.lang.String) wrxOf.get(declId),
354
                                       #(java.lang.String) sectOf.get(declId),
355
                                       null)</action>
356

  
357
               <rule>ident != null
358
                  <action>originOf.put(declId, ident)</action>
359
                  <action>kindOf.put(declId, "ocx-control-frame")</action>
360
                  <action>putReferenceNote(declId, "com-origin", ident)</action>
361
                  <action>putReferenceNote(declId, "com-origin-kind", "ocx-control-frame")</action>
362
               </rule>
363
            </rule>
364
         </rule>
365
      </rule>
366

  
367
      <!-- ================================================================================
368
           Origin inheritance through assignment:
369
              chCtl  = chFrame:ListView.            (named OCX control off a control frame)
370
              chBook = chExcel:Workbooks:Add().     (object returned from a COM chain)
371
              chSheet = chBook:Worksheets(5).       (transitively, from an inherited origin)
372
           The assigned handle takes the identity of the referent the chain is rooted at. For a
373
           control frame the member name is the control's name and refines the identity; for an
374
           automation object the identity is inherited unchanged, since the intermediate objects
375
           a chain walks through have no control identity of their own.
376
           ================================================================================ -->
377
      <rule>(type == prog.assignment or type == prog.assign)   and
378
            this.getChildAt(0) != null                         and
379
            this.getChildAt(1) != null
380

  
381
         <action>lref = execLib("expr_payload", this.getChildAt(0))</action>
382
         <action>rref = execLib("expr_payload", this.getChildAt(1))</action>
383

  
384
         <rule>lref != null                                 and
385
               rref != null                                 and
386
               evalLib("com_handle_type", lref.type)        and
387
               (rref.type == prog.com_invocation       or
388
                rref.type == prog.colon)
389

  
390
            <action>href = execLib("com_root_ref", rref)</action>
391

  
392
            <rule>href != null and evalLib("com_handle_type", href.type)
393
               <action>srcId  = execLib("decl_id", href)</action>
394
               <action>declId = execLib("decl_id", lref)</action>
395

  
396
               <rule>srcId != null and declId != null and declId != srcId
397
                  <action>ident = #(java.lang.String) originOf.get(srcId)</action>
398

  
399
                  <action>pnode = rref.getChildAt(1)</action>
400
                  <action>skind = #(java.lang.String) kindOf.get(srcId)</action>
401

  
402
                  <rule>ident != null
403

  
404
                     <!-- A control frame yields a NAMED control, so the member name is the
405
                          control's name and refines the identity. Stated as two mutually
406
                          exclusive sibling rules rather than one rule with an on="false" branch,
407
                          because the branch would otherwise have to sit after a nested rule and
408
                          its scope becomes ambiguous. -->
409
                     <rule>skind != null                          and
410
                           skind.equals("ocx-control-frame")     and
411
                           pnode != null
412
                        <action>ident = execLib("compose_ocx",
413
                                                #(java.lang.String) frameOf.get(srcId),
414
                                                #(java.lang.String) wrxOf.get(srcId),
415
                                                #(java.lang.String) sectOf.get(srcId),
416
                                                pnode.text)</action>
417
                        <action>originOf.put(declId, ident)</action>
418
                        <action>kindOf.put(declId, "ocx-control")</action>
419
                        <action>putReferenceNote(declId, "com-origin", ident)</action>
420
                        <action>putReferenceNote(declId, "com-origin-kind", "ocx-control")</action>
421
                     </rule>
422

  
423
                     <!-- otherwise the origin is inherited verbatim from the chain root, since the
424
                          intermediate objects a chain walks through have no identity of their own -->
425
                     <rule>skind == null                          or
426
                           !skind.equals("ocx-control-frame")    or
427
                           pnode == null
428
                        <action>originOf.put(declId, ident)</action>
429
                        <action>kindOf.put(declId, "com-derived")</action>
430
                        <action>putReferenceNote(declId, "com-origin", ident)</action>
431
                        <action>putReferenceNote(declId, "com-origin-kind", "com-derived")</action>
432
                     </rule>
433
                  </rule>
434
               </rule>
435
            </rule>
436
         </rule>
437
      </rule>
438

  
439
   </walk-rules>
440

  
441
</rule-set>
new/rules/annotations/early_annotations.xml 2026-08-31 10:14:50 +0000
19 19
** 009 DDF 20250207 Added annotations/early_javanames rule set.
20 20
** 010 DDF 20250424 Replaced file with artifact.
21 21
** 011 AOG 20250818 Added multiThreadAccess flag to the global shared variables between conversion threads.
22
** 012 HC  20260831 Added annotations/com_origin rule set. Refs #11808.
22 23
*/
23 24
 -->
24 25
 
......
222 223
   <!-- Annotate abbreviations in tables/fields -->
223 224
   <rule-set name="annotations/abbrev_check" />
224 225

  
226
   <!-- Associate each COM/OCX referent with its originating control or object -->
227
   <rule-set name="annotations/com_origin" />
228

  
225 229
   <!-- persist -->
226 230
   <rule-set>
227 231
      <post-rules>
new/rules/include/report.rules 2026-08-31 17:14:21 +0000
63 63
** 030 OM  20230115          Replaced absolutePath(), relativePath(), upPath() and downPath() with faster
64 64
**                           versions, based on node types.
65 65
** 031 GES 20241217          Added helper to find all codepage references.
66
** 032 PBB 20250702          Fixed the problem with the describe_extent_lvalue function where it would  
66
** 032 PBB 20250702          Fixed the problem with the describe_extent_lvalue function where it would
67 67
**                           fail for field types.
68
** 033 HC  20260831          Added helpers to attribute COM/OCX method and property accesses to the
69
**                           control or object they originate from. Refs #11808.
68 70
*/
69 71
 -->
70 72
 
......
906 908
         </rule>
907 909
      </function>
908 910
      
911
      <!-- Descend the left edge of a COM invocation chain to the referent it is rooted at. For
912
           chExcel:Workbooks:Add() this yields the chExcel reference. Note that get_lvalue() is
913
           NOT a substitute: it descends child 1 and therefore lands on a member node rather than
914
           on the chain's root referent. -->
915
      <function name="com_chain_root">
916
         <parameter name="target" type="com.goldencode.ast.Aast" />
917
         <return    name="ref"    type="com.goldencode.ast.Aast" />
918

  
919
         <rule>ref = target</rule>
920

  
921
         <while>ref != null                            and
922
                (ref.type == prog.com_invocation  or
923
                 ref.type == prog.colon)
924
            <action>ref = ref.getChildAt(0)</action>
925
         </while>
926
      </function>
927

  
928
      <!-- For an access made THROUGH a named OCX control hanging off a control frame, the AST node
929
           naming that control; null otherwise.
930

  
931
           In real AppBuilder code a named control is never assigned to its own COM-HANDLE; it is
932
           reached as a chained property off the frame handle, e.g.
933
           chCfTreeView:TreeView:Nodes:Add(...). The control name is therefore the innermost hop of
934
           the chain, and it belongs in the control's identity rather than in the member path,
935
           because the same .wrx section can expose several controls and a control name on its own
936
           is not unique across sections.
937

  
938
           The innermost hop is only a control name when the access reaches through it. When the
939
           member IS the innermost hop the access is made on the frame's own COM object (most
940
           importantly LoadControls(), but equally a bare control lookup such as chFrame:ListView)
941
           and it stays attributed to the frame. -->
942
      <function name="com_frame_hop">
943
         <variable name="inv"  type="com.goldencode.ast.Aast" />
944
         <variable name="ref"  type="com.goldencode.ast.Aast" />
945
         <variable name="decl" type="com.goldencode.ast.Aast" />
946
         <variable name="kind" type="java.lang.String" />
947
         <return   name="hop"  type="com.goldencode.ast.Aast" />
948

  
949
         <rule>hop = null</rule>
950
         <rule>inv = parent</rule>
951

  
952
         <!-- descend to the innermost invocation of the chain -->
953
         <while>inv.getChildAt(0) != null                       and
954
                inv.getChildAt(0).type == prog.com_invocation
955
            <action>inv = inv.getChildAt(0)</action>
956
         </while>
957

  
958
         <rule>inv.getId() != parent.getId()
959
            <action>ref = inv.getChildAt(0)</action>
960

  
961
            <rule>ref != null
962
               <action>decl = ref</action>
963

  
964
               <rule>ref.isAnnotation("refid")
965
                  <action>decl = getAst(#(long) ref.getAnnotation("refid"))</action>
966
               </rule>
967

  
968
               <rule>decl != null and decl.isAnnotation("com-origin-kind")
969
                  <action>kind = #(java.lang.String) decl.getAnnotation("com-origin-kind")</action>
970

  
971
                  <rule>kind != null and kind.equals("ocx-control-frame")
972
                     <action>hop = inv.getChildAt(1)</action>
973
                  </rule>
974
               </rule>
975
            </rule>
976
         </rule>
977
      </function>
978

  
979
      <!-- True for a COM/OCX method call or property access. -->
980
      <function name="com_member_access">
981
         <return name="is_acc" type="java.lang.Boolean" />
982

  
983
         <rule>is_acc = false</rule>
984

  
985
         <rule>parent.type == prog.com_invocation        and
986
               (this.type == prog.com_method        or
987
                this.type == prog.com_property)
988
            <action>is_acc = true</action>
989
         </rule>
990
      </function>
991

  
992
      <!-- The control or automation object this access is made against, as recorded on the
993
           referent's declaration by annotations/com_origin during the F2 front end. Accesses whose
994
           origin could not be resolved statically (handle received as a parameter, through a
995
           shared variable, from a computed ProgID, or from another compilation unit) are reported
996
           as UNRESOLVED rather than dropped. -->
997
      <function name="describe_com_control">
998
         <variable name="ref"  type="com.goldencode.ast.Aast" />
999
         <variable name="decl" type="com.goldencode.ast.Aast" />
1000
         <variable name="hop"  type="com.goldencode.ast.Aast" />
1001
         <return   name="txt"  type="java.lang.String" />
1002

  
1003
         <rule>txt = "UNRESOLVED"</rule>
1004

  
1005
         <rule>ref = execLib("com_chain_root", parent.getChildAt(0))</rule>
1006

  
1007
         <rule>ref != null
1008
            <action>decl = ref</action>
1009

  
1010
            <rule>ref.isAnnotation("refid")
1011
               <action>decl = getAst(#(long) ref.getAnnotation("refid"))</action>
1012
            </rule>
1013

  
1014
            <rule>decl != null and decl.isAnnotation("com-origin")
1015
               <action>txt = #(java.lang.String) decl.getAnnotation("com-origin")</action>
1016

  
1017
               <!-- qualify a control frame's identity with the named control the access is made
1018
                    through, so the key identifies the control and not just the .wrx section -->
1019
               <action>hop = execLib("com_frame_hop")</action>
1020

  
1021
               <rule>hop != null
1022
                  <action>txt = sprintf("%s/%s", txt, hop.text)</action>
1023
               </rule>
1024
            </rule>
1025
         </rule>
1026
      </function>
1027

  
1028
      <!-- The member being accessed, qualified by the chain it is reached through and classified
1029
           by kind. A method's argument count includes omitted argument slots, since COM treats an
1030
           omitted argument as a distinct, meaningful case. Examples:
1031
              Visible [PROPERTY SET]
1032
              Workbooks [PROPERTY GET]
1033
              Workbooks:Add() [METHOD, 0 args]
1034
              Columns:Font:ColorIndex [PROPERTY SET] -->
1035
      <function name="describe_com_member">
1036
         <variable name="cur"   type="com.goldencode.ast.Aast" />
1037
         <variable name="mem"   type="com.goldencode.ast.Aast" />
1038
         <variable name="hop"   type="com.goldencode.ast.Aast" />
1039
         <variable name="nargs" type="java.lang.Integer" />
1040
         <variable name="isset" type="java.lang.Boolean" />
1041
         <return   name="txt"   type="java.lang.String" />
1042

  
1043
         <!-- qualify the member with the members of the chain it hangs off of, except for a named
1044
              OCX control, which identifies the control itself and is carried by the control key -->
1045
         <rule>txt = text</rule>
1046
         <rule>hop = execLib("com_frame_hop")</rule>
1047
         <rule>cur = parent.getChildAt(0)</rule>
1048

  
1049
         <while>cur != null and cur.type == prog.com_invocation
1050
            <action>mem = cur.getChildAt(1)</action>
1051

  
1052
            <rule>mem != null                              and
1053
                  (hop == null                         or
1054
                   mem.getId() != hop.getId())
1055
               <action>txt = sprintf("%s:%s", mem.text, txt)</action>
1056
            </rule>
1057

  
1058
            <action>cur = cur.getChildAt(0)</action>
1059
         </while>
1060

  
1061
         <rule>this.type == prog.com_method
1062

  
1063
            <!-- Argument slots, not argument expressions: COM treats an omitted argument as a
1064
                 distinct and meaningful case, so f(, x) is deliberately reported as 2. The one
1065
                 exception is an empty argument list, which the grammar still models as a single
1066
                 empty COM_PARAMETER; f() has no arguments and must report 0. -->
1067
            <action>nargs = this.getNumImmediateChildren(prog.com_parameter)</action>
1068

  
1069
            <rule>nargs == 1
1070
               <action>cur = this.getImmediateChild(prog.com_parameter, null)</action>
1071

  
1072
               <rule>cur != null and cur.getNumImmediateChildren() == 0
1073
                  <action>nargs = 0</action>
1074
               </rule>
1075
            </rule>
1076

  
1077
            <action>txt = sprintf("%s() [METHOD, %d args]", txt, nargs)</action>
1078
         </rule>
1079

  
1080
         <rule>this.type == prog.com_property
1081

  
1082
            <!-- A property is written only when ITS OWN invocation is the left hand side of an
1083
                 assignment. Every inner hop of a chain is a read: in
1084
                 chExcel:Columns:Font:Bold = TRUE only Bold is written, while Columns and Font are
1085
                 read on the way to it. Testing the outermost invocation instead would mark every
1086
                 hop in the chain as a write. -->
1087
            <action>isset = false</action>
1088

  
1089
            <rule>parent.parent != null                          and
1090
                  (parent.parent.type == prog.assign         or
1091
                   parent.parent.type == prog.assignment)        and
1092
                  parent.indexPos == 0
1093
               <action>isset = true</action>
1094
            </rule>
1095

  
1096
            <rule>isset
1097
               <action>txt = sprintf("%s [PROPERTY SET]", txt)</action>
1098
               <action on="false">txt = sprintf("%s [PROPERTY GET]", txt)</action>
1099
            </rule>
1100
         </rule>
1101
      </function>
1102

  
909 1103
      <function name="wrx_file_reference">
910 1104
         <parameter  name="ref"    type="com.goldencode.ast.Aast" />
911 1105
         <return     name="is_wrx"   type="java.lang.Boolean" />
new/rules/reports/profile.rpt 2026-08-31 10:17:02 +0000
74 74
** 038 GES 20220429          Added reports for class defs with serializable and use-widget-pool.
75 75
** 039 RFB 20241106          Added reports for abbreviated field names and abbreviated table names.
76 76
** 040 GES 20241217          Added reports for catch blocks, codepage references, dslog-manager.
77
** 041 HC  20260831          Added COM/OCX API usage reports, which attribute every method and
78
**                           property access to the control or object it originates from.
79
**                           Refs #11808.
77 80
*/
78 81
 -->
79 82
 
......
659 662
      dumpLevel="1"
660 663
      title="OCX Usage by File"
661 664
      tags="User Interface,Platform Specific"/>
662
   <report 
665
   <report
666
      condition="evalLib(&quot;com_member_access&quot;)"
667
      dumpExpr="execLib(&quot;describe_com_member&quot;)"
668
      multiplexExpr="execLib(&quot;describe_com_control&quot;)"
669
      title="COM/OCX API Usage by Control"
670
      tags="Base Language,Expressions,User Interface,Platform Specific"/>
671
   <report
672
      condition="lastMatch"
673
      dumpExpr="execLib(&quot;describe_com_control&quot;)"
674
      multiplexExpr="execLib(&quot;describe_com_member&quot;)"
675
      title="COM/OCX API Usage by Member"
676
      tags="Base Language,Expressions,User Interface,Platform Specific"/>
677
   <report
678
      condition="lastMatch"
679
      dumpType="parser"
680
      dumpLevel="1"
681
      multiplexExpr="sprintf(&quot;%s :: %s&quot;, execLib(&quot;describe_com_control&quot;), execLib(&quot;describe_com_member&quot;))"
682
      title="COM/OCX API Usage by Control and Member"
683
      tags="Base Language,Expressions,User Interface,Platform Specific"/>
684
   <report
685
      condition="lastMatch"
686
      dumpExpr="sprintf(&quot;%s :: %s&quot;, execLib(&quot;describe_com_control&quot;), execLib(&quot;describe_com_member&quot;))"
687
      multiplexExpr="file"
688
      title="COM/OCX API Usage by File"
689
      tags="Base Language,Expressions,User Interface,Platform Specific"/>
690
   <report
691
      condition="evalLib(&quot;com_member_access&quot;) and execLib(&quot;describe_com_control&quot;).equals(&quot;UNRESOLVED&quot;)"
692
      dumpType="parser"
693
      dumpLevel="1"
694
      multiplexExpr="execLib(&quot;describe_com_member&quot;)"
695
      title="COM/OCX Accesses With Unresolved Control Origin"
696
      tags="Base Language,Expressions,User Interface,Platform Specific"/>
697
   <report
663 698
      condition="evalLib(&quot;complex_extent_expr&quot;)"
664 699
      dumpType="parser"
665 700
      multiplexExpr="execLib(&quot;describe_node&quot;, this, true)"