Summary
Two method-search queries accept an environmentId and then ignore it, so they always search
method environment 0. Every caller loops 0..gemstone.maxEnvironment over them, which means a
method that lives in a higher environment is invisible to the search — and the loop does N
identical full-image scans to produce the same environment-0 answer each time.
Both are the same species of bug and the same shape of fix, so they belong together.
1. referencesToObject builds its organizer without an environment
client/src/queries/methodSearch.ts — referencesToObject
methods := (ClassOrganizer new referencesToObject:
(System myUserProfile symbolList objectNamed: #'...')).
The environmentId argument reaches methodSerialization(environmentId) only. The organizer is
built bare, so the scan itself always runs in environment 0. Compare sendersOf a few lines
above, which correctly does ClassOrganizer new environmentId: N; yourself.
Affected callers, all of which sweep environments:
- Browse References (
extension.ts, gemstone.browseReferences)
- GemStone Search, references pivot (
omniSearch/omniSearchCommand.ts)
- the MCP references tool (
mcpTools.ts)
Minor, same function: the result is handed to methodSerialization without asArray, which then
indexes it with at: i.
2. hierarchyImplementorsOf selects with environment-0-only reflection
client/src/queries/methodSearch.ts — hierarchyImplementorsOf
(cur includesSelector: #'sel') ifTrue: [methods add: (cur compiledMethodAt: #'sel')].
includesSelector: and the bare compiledMethodAt: are both environment-0 only, so an
implementor that exists only in a higher environment is never collected — in either direction of
the walk. Its caller (gemstone.hierarchyImplementorsOf in extension.ts) loops environments
over it, so superclass implementors and subclass overrides outside environment 0 are invisible
where that command is used.
Confirmed on a live stone
PrbUser>>usesInEnv0 compiled into environment 0 and PrbUser>>usesInEnv1 into environment 1,
both referencing PrbTarget:
| expression |
answer |
ClassOrganizer new referencesToObject: PrbTarget |
PrbUser>>usesInEnv0 |
(ClassOrganizer new environmentId: 1; yourself) referencesToObject: PrbTarget |
PrbUser>>usesInEnv1 |
PrbUser includesSelector: #usesInEnv1 |
false |
PrbUser compiledMethodAt: #usesInEnv1 environmentId: 1 otherwise: nil |
the method |
PrbUser selectorsForEnvironment: 1 |
#(usesInEnv1) |
So the underlying reflection honours the environment in every case; these two queries just never
pass it on.
Suggested fix
- Put the environment on the organizer:
((ClassOrganizer new environmentId: N; yourself) referencesToObject: obj) asArray.
- In the hierarchy walk, select per environment —
compiledMethodAt:environmentId:otherwise:
and test the result for nil, rather than gating on includesSelector:.
Why this is filed separately
The same two bugs were fixed for the safe-delete guard in
#433, where a missed reference meant a class or
variable could be deleted while the user was told nothing referenced it. That work deliberately
did not touch these two, because they are pre-existing, already shipped, and affect commands
outside that feature. The fixes there are a working model for these:
- scanning references to a class now sets the environment on the organizer;
- the instance- and class-variable scans now enumerate with
selectorsForEnvironment: instead of
selectors, which has the same environment-0-only limitation as includesSelector:;
- found methods now carry the environment they were found in, so a selector implemented in two
environments counts as two methods and each opens the document it actually lives in.
That last point is worth pairing with this work: the result rows produced by these two queries
feed the same shared picker, which until #433 opened every result as environment 0.
Summary
Two method-search queries accept an
environmentIdand then ignore it, so they always searchmethod environment 0. Every caller loops
0..gemstone.maxEnvironmentover them, which means amethod that lives in a higher environment is invisible to the search — and the loop does N
identical full-image scans to produce the same environment-0 answer each time.
Both are the same species of bug and the same shape of fix, so they belong together.
1.
referencesToObjectbuilds its organizer without an environmentclient/src/queries/methodSearch.ts—referencesToObjectThe
environmentIdargument reachesmethodSerialization(environmentId)only. The organizer isbuilt bare, so the scan itself always runs in environment 0. Compare
sendersOfa few linesabove, which correctly does
ClassOrganizer new environmentId: N; yourself.Affected callers, all of which sweep environments:
extension.ts,gemstone.browseReferences)omniSearch/omniSearchCommand.ts)mcpTools.ts)Minor, same function: the result is handed to
methodSerializationwithoutasArray, which thenindexes it with
at: i.2.
hierarchyImplementorsOfselects with environment-0-only reflectionclient/src/queries/methodSearch.ts—hierarchyImplementorsOfincludesSelector:and the barecompiledMethodAt:are both environment-0 only, so animplementor that exists only in a higher environment is never collected — in either direction of
the walk. Its caller (
gemstone.hierarchyImplementorsOfinextension.ts) loops environmentsover it, so superclass implementors and subclass overrides outside environment 0 are invisible
where that command is used.
Confirmed on a live stone
PrbUser>>usesInEnv0compiled into environment 0 andPrbUser>>usesInEnv1into environment 1,both referencing
PrbTarget:ClassOrganizer new referencesToObject: PrbTargetPrbUser>>usesInEnv0(ClassOrganizer new environmentId: 1; yourself) referencesToObject: PrbTargetPrbUser>>usesInEnv1PrbUser includesSelector: #usesInEnv1falsePrbUser compiledMethodAt: #usesInEnv1 environmentId: 1 otherwise: nilPrbUser selectorsForEnvironment: 1#(usesInEnv1)So the underlying reflection honours the environment in every case; these two queries just never
pass it on.
Suggested fix
((ClassOrganizer new environmentId: N; yourself) referencesToObject: obj) asArray.compiledMethodAt:environmentId:otherwise:and test the result for nil, rather than gating on
includesSelector:.Why this is filed separately
The same two bugs were fixed for the safe-delete guard in
#433, where a missed reference meant a class or
variable could be deleted while the user was told nothing referenced it. That work deliberately
did not touch these two, because they are pre-existing, already shipped, and affect commands
outside that feature. The fixes there are a working model for these:
selectorsForEnvironment:instead ofselectors, which has the same environment-0-only limitation asincludesSelector:;environments counts as two methods and each opens the document it actually lives in.
That last point is worth pairing with this work: the result rows produced by these two queries
feed the same shared picker, which until #433 opened every result as environment 0.