Files
zzet--gortex/internal/parser/languages/java_rn.go
T
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

168 lines
4.9 KiB
Go

package languages
import (
"regexp"
"strings"
)
// React Native JVM native-module support. A Java native module is a
// class whose @ReactMethod-annotated methods are callable from JS as
// `NativeModules.<module>.<method>(...)`. The JS module name is the
// string the class's getName() returns (or a @ReactModule(name=...)
// override), defaulting to the class name.
var (
javaClassDeclRe = regexp.MustCompile(`\bclass\s+([A-Za-z_]\w*)`)
javaGetNameReturnRe = regexp.MustCompile(`getName\s*\(\s*\)\s*\{[^{}]*return\s+"([^"]+)"`)
javaReactModuleNmRe = regexp.MustCompile(`@ReactModule\s*\(\s*name\s*=\s*"([^"]+)"`)
)
// extractJavaRNModuleNames maps each class name in src to the JS module
// name it exposes to React Native. Resolution order (later wins):
// class name, getName() string-literal return, @ReactModule(name=...).
func extractJavaRNModuleNames(src []byte) map[string]string {
s := string(src)
out := map[string]string{}
prevEnd := 0
for _, loc := range javaClassDeclRe.FindAllStringSubmatchIndex(s, -1) {
className := s[loc[2]:loc[3]]
module := className
// Class body span (balanced braces) for getName() inspection.
bodyOpen := strings.IndexByte(s[loc[1]:], '{')
bodyEnd := len(s)
if bodyOpen >= 0 {
bodyOpen += loc[1]
if e := javaMatchBrace(s, bodyOpen); e > bodyOpen {
bodyEnd = e
if m := javaGetNameReturnRe.FindStringSubmatch(s[bodyOpen:bodyEnd]); m != nil {
module = m[1]
}
}
}
// @ReactModule(name=...) sits in the annotation band just before
// the class keyword — search the gap since the previous class.
// Clamp the lower bound: for a nested class the enclosing
// class's body span (recorded in prevEnd) extends past this
// class's start, so prevEnd > loc[0] and the slice would panic.
bandStart := min(prevEnd, loc[0])
if m := javaReactModuleNmRe.FindStringSubmatch(s[bandStart:loc[0]]); m != nil {
module = m[1]
}
out[className] = module
// Track the furthest body end seen so a nested class (whose own
// body ends before its enclosing class's) can't rewind prevEnd
// and re-scan an outer class's annotations.
prevEnd = max(prevEnd, bodyEnd)
}
return out
}
// javaMatchBrace returns the index just past the '}' matching the '{' at
// openIdx, or -1 when unbalanced.
func javaMatchBrace(s string, openIdx int) int {
if openIdx < 0 || openIdx >= len(s) || s[openIdx] != '{' {
return -1
}
depth := 0
for i := openIdx; i < len(s); i++ {
switch s[i] {
case '{':
depth++
case '}':
depth--
if depth == 0 {
return i + 1
}
}
}
return -1
}
// javaHasReactMethod reports whether any annotation is @ReactMethod.
func javaHasReactMethod(annos []javaAnnotation) bool {
for _, a := range annos {
if a.name == "ReactMethod" {
return true
}
}
return false
}
// javaReactPropRe matches @ReactProp(name = "x") — the marker of a React
// Native view-manager property.
var javaReactPropRe = regexp.MustCompile(`@ReactProp\s*\(\s*name\s*=\s*"([^"]+)"`)
// javaFabricManager is a native view manager discovered in a Java file.
type javaFabricManager struct {
component string
props []string
line int
}
// extractJavaFabricManagers finds classes carrying @ReactProp methods
// (React Native view managers) and resolves the JS component name from
// getName() / @ReactModule (rnModules) or the class name, stripping the
// RN "Manager" suffix. rnModules is the class→module map from
// extractJavaRNModuleNames.
func extractJavaFabricManagers(src []byte, rnModules map[string]string) []javaFabricManager {
s := string(src)
propLocs := javaReactPropRe.FindAllStringSubmatchIndex(s, -1)
if len(propLocs) == 0 {
return nil
}
type span struct {
name string
start, end int
}
var spans []span
for _, loc := range javaClassDeclRe.FindAllStringSubmatchIndex(s, -1) {
className := s[loc[2]:loc[3]]
end := len(s)
if bodyOpen := strings.IndexByte(s[loc[1]:], '{'); bodyOpen >= 0 {
bodyOpen += loc[1]
if e := javaMatchBrace(s, bodyOpen); e > bodyOpen {
end = e
}
}
spans = append(spans, span{name: className, start: loc[0], end: end})
}
classForOff := func(off int) (string, bool) {
for _, sp := range spans {
if off >= sp.start && off < sp.end {
return sp.name, true
}
}
return "", false
}
propsByClass := map[string][]string{}
lineByClass := map[string]int{}
var order []string
for _, loc := range propLocs {
cls, ok := classForOff(loc[0])
if !ok {
continue
}
if _, seen := propsByClass[cls]; !seen {
order = append(order, cls)
lineByClass[cls] = lineAt(src, loc[0])
}
propsByClass[cls] = append(propsByClass[cls], s[loc[2]:loc[3]])
}
var out []javaFabricManager
for _, cls := range order {
comp := cls
if m := rnModules[cls]; m != "" {
comp = m
}
out = append(out, javaFabricManager{
component: objcComponentName(comp), // shared "Manager"-suffix stripper
props: propsByClass[cls],
line: lineByClass[cls],
})
}
return out
}