a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
133 lines
4.0 KiB
Go
133 lines
4.0 KiB
Go
package resolver
|
|
|
|
import (
|
|
"sort"
|
|
|
|
"github.com/zzet/gortex/internal/graph"
|
|
)
|
|
|
|
// rnNativePairVia marks a synthesized iOS↔Android native-implementation pairing
|
|
// edge.
|
|
const rnNativePairVia = "rn.native.pair"
|
|
|
|
// ResolveReactNativeNativePairing is the framework-dispatch synthesizer that
|
|
// links the two native implementations of a classic React Native module. A
|
|
// module is implemented twice — once on iOS (Objective-C / Swift,
|
|
// RCT_EXPORT_METHOD) and once on Android (Java / Kotlin, @ReactMethod) — and
|
|
// both extractors stamp the same rn_module + rn_method on their method nodes.
|
|
// For each (module, method) implemented on both platforms this pass
|
|
// synthesizes a pair of EdgeReferences edges (one each way) between the iOS and
|
|
// Android method, so navigating from one platform's implementation surfaces the
|
|
// other — the parallel-implementation relationship the static call graph can't
|
|
// see because the two sides never call each other.
|
|
//
|
|
// Only cross-platform pairs are linked (iOS↔Android); two implementations on
|
|
// the same platform are not paired. Full recompute and idempotent: edges are
|
|
// re-derived from the rn metadata, graph.AddEdge dedupes by key, and
|
|
// graph.EvictFile drops the pairing when either side's file is reindexed. Edges
|
|
// ride at ast_inferred and carry synthesizer provenance.
|
|
//
|
|
// Returns the number of cross-platform method pairs linked.
|
|
func ResolveReactNativeNativePairing(g graph.Store) int {
|
|
if g == nil {
|
|
return 0
|
|
}
|
|
|
|
type modKey struct{ module, method string }
|
|
nativeByKey := map[modKey][]*graph.Node{}
|
|
for _, n := range nodesByKindsOrAll(g, graph.KindMethod) {
|
|
if n == nil || n.Meta == nil {
|
|
continue
|
|
}
|
|
mod, _ := n.Meta["rn_module"].(string)
|
|
meth, _ := n.Meta["rn_method"].(string)
|
|
if mod == "" || meth == "" {
|
|
continue
|
|
}
|
|
nativeByKey[modKey{mod, meth}] = append(nativeByKey[modKey{mod, meth}], n)
|
|
}
|
|
if len(nativeByKey) == 0 {
|
|
return 0
|
|
}
|
|
|
|
keys := make([]modKey, 0, len(nativeByKey))
|
|
for k := range nativeByKey {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Slice(keys, func(i, j int) bool {
|
|
if keys[i].module != keys[j].module {
|
|
return keys[i].module < keys[j].module
|
|
}
|
|
return keys[i].method < keys[j].method
|
|
})
|
|
|
|
var batch []*graph.Edge
|
|
paired := 0
|
|
for _, k := range keys {
|
|
impls := nativeByKey[k]
|
|
for i := 0; i < len(impls); i++ {
|
|
for j := i + 1; j < len(impls); j++ {
|
|
a, b := impls[i], impls[j]
|
|
if a.ID == "" || b.ID == "" || a.ID == b.ID {
|
|
continue
|
|
}
|
|
if !rnDifferentPlatform(a.Language, b.Language) {
|
|
continue
|
|
}
|
|
batch = append(batch,
|
|
rnNativePairEdge(a, b, k.module, k.method),
|
|
rnNativePairEdge(b, a, k.module, k.method),
|
|
)
|
|
paired++
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, e := range batch {
|
|
g.AddEdge(e)
|
|
}
|
|
return paired
|
|
}
|
|
|
|
// rnNativePairEdge builds one direction of the cross-platform pairing.
|
|
func rnNativePairEdge(from, to *graph.Node, module, method string) *graph.Edge {
|
|
return &graph.Edge{
|
|
From: from.ID,
|
|
To: to.ID,
|
|
Kind: graph.EdgeReferences,
|
|
FilePath: from.FilePath,
|
|
Line: from.StartLine,
|
|
Confidence: 0.6,
|
|
ConfidenceLabel: graph.ConfidenceLabelFor(graph.EdgeReferences, 0.6),
|
|
Origin: graph.OriginASTInferred,
|
|
Meta: map[string]any{
|
|
"via": rnNativePairVia,
|
|
"rn_module": module,
|
|
"rn_method": method,
|
|
"native_platform": rnPlatform(to.Language),
|
|
MetaSynthesizedBy: SynthReactNativePair,
|
|
MetaProvenance: ProvenanceHeuristic,
|
|
},
|
|
}
|
|
}
|
|
|
|
// rnPlatform maps a native language to the React Native platform it
|
|
// implements: ios (Objective-C / Swift) or android (Java / Kotlin); "" for
|
|
// anything else.
|
|
func rnPlatform(lang string) string {
|
|
switch lang {
|
|
case "objc", "swift":
|
|
return "ios"
|
|
case "java", "kotlin":
|
|
return "android"
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// rnDifferentPlatform reports whether two native languages implement opposite
|
|
// React Native platforms, so only iOS↔Android pairs are linked.
|
|
func rnDifferentPlatform(a, b string) bool {
|
|
pa, pb := rnPlatform(a), rnPlatform(b)
|
|
return pa != "" && pb != "" && pa != pb
|
|
}
|