Summary
In Python code that talks to InterSystems IRIS, iris.cls("Pkg.X").Method() is
the standard way to invoke an ObjectScript class method. CBM creates a CALLS
edge for these but resolves the target by bare method name only, discarding the
class-name string literal.
Consequences:
- calls resolve to the wrong class when two classes share a method name
- calls to a class that does not exist in the graph still produce an edge
- unrelated receivers (
anything.Run(...)) produce an edge to whichever class
happens to define Run
db.classMethodValue("Pkg.X", "Method", ...), the lower-level form this API is
meant to replace, resolves correctly and class-aware. So the form the ecosystem
is being encouraged to adopt is the one handled worst.
Reproduction
Repo with two ObjectScript classes, Pkg.A and Pkg.B, each defining only:
ClassMethod Run(x As %String) As %String
{
Quit x
}
and one caller.py:
import iris
def want_a():
return iris.cls("Pkg.A").Run("x")
def want_b():
return iris.cls("Pkg.B").Run("x")
def want_nonexistent():
return iris.cls("Pkg.DoesNotExist").Run("x")
def unrelated_receiver():
return some_random_thing.Run("x")
def via_classmethodvalue(db):
return db.classMethodValue("Pkg.B", "Run", "x")
Query:
MATCH (a)-[r:CALLS]->(b) RETURN a.name, b.qualified_name
Observed:
unrelated_receiver Pkg.A.Run <-- false edge
via_classmethodvalue Pkg.B.Run <-- correct (class-aware path)
want_a Pkg.A.Run <-- correct, but only coincidentally
want_b Pkg.A.Run <-- WRONG CLASS
want_nonexistent Pkg.A.Run <-- false edge
Four of the five iris.cls and bare-receiver edges are wrong. The one correct
row is the classMethodValue form.
Root cause
iris.cls(...) is not recognized as a dispatch receiver, so the call falls
through to generic bare-method-name matching. The class string sits right there
as a literal argument at the call site and is never consulted.
The machinery already exists for the sibling API.
internal/cbm/extract_calls.c:65 defines s_py_dispatch_suffixes for
classMethodValue / classMethodVoid / classMethodBoolean /
classMethodObject, resolved class-aware at :2301-2318.
Suggested fix
- When a call's receiver is itself a call to
iris.cls / cls with a
string-literal first argument, synthesize the callee qualified name as
"<class-literal>.<method>" and resolve it the way classMethodValue
already is.
- Suppress the generic bare-name fallback for that call node, so an
unresolvable class yields no edge rather than a wrong one.
- Separately,
invokeClassMethod is missing from s_py_dispatch_suffixes
(extract_calls.c:65-66) though it is documented alongside the
classMethod* family. One-line addition, same resolution path.
Item 2 matters most. A missing edge is recoverable; a confidently wrong edge is
not.
Scope note
This sits entirely within CBM's own extraction code, no vendored files
involved. It is independent of Issues A and C and can land on its own.
Tests
I'd add cases to the existing Python call-extraction group in
tests/test_extraction.c covering correct class selection with two same-named
methods, no edge for an unknown class literal, no edge for a non-iris.cls
receiver, and invokeClassMethod resolution.
Happy to open a PR once you confirm the approach, particularly the
"suppress the bare-name fallback" decision, since it makes CBM emit strictly
fewer edges here and I want to be sure that's the behavior you want.
Summary
In Python code that talks to InterSystems IRIS,
iris.cls("Pkg.X").Method()isthe standard way to invoke an ObjectScript class method. CBM creates a
CALLSedge for these but resolves the target by bare method name only, discarding the
class-name string literal.
Consequences:
anything.Run(...)) produce an edge to whichever classhappens to define
Rundb.classMethodValue("Pkg.X", "Method", ...), the lower-level form this API ismeant to replace, resolves correctly and class-aware. So the form the ecosystem
is being encouraged to adopt is the one handled worst.
Reproduction
Repo with two ObjectScript classes,
Pkg.AandPkg.B, each defining only:and one
caller.py:Query:
Observed:
Four of the five
iris.clsand bare-receiver edges are wrong. The one correctrow is the
classMethodValueform.Root cause
iris.cls(...)is not recognized as a dispatch receiver, so the call fallsthrough to generic bare-method-name matching. The class string sits right there
as a literal argument at the call site and is never consulted.
The machinery already exists for the sibling API.
internal/cbm/extract_calls.c:65definess_py_dispatch_suffixesforclassMethodValue/classMethodVoid/classMethodBoolean/classMethodObject, resolved class-aware at:2301-2318.Suggested fix
iris.cls/clswith astring-literal first argument, synthesize the callee qualified name as
"<class-literal>.<method>"and resolve it the wayclassMethodValuealready is.
unresolvable class yields no edge rather than a wrong one.
invokeClassMethodis missing froms_py_dispatch_suffixes(
extract_calls.c:65-66) though it is documented alongside theclassMethod*family. One-line addition, same resolution path.Item 2 matters most. A missing edge is recoverable; a confidently wrong edge is
not.
Scope note
This sits entirely within CBM's own extraction code, no vendored files
involved. It is independent of Issues A and C and can land on its own.
Tests
I'd add cases to the existing Python call-extraction group in
tests/test_extraction.ccovering correct class selection with two same-namedmethods, no edge for an unknown class literal, no edge for a non-
iris.clsreceiver, and
invokeClassMethodresolution.Happy to open a PR once you confirm the approach, particularly the
"suppress the bare-name fallback" decision, since it makes CBM emit strictly
fewer edges here and I want to be sure that's the behavior you want.