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
147 lines
5.6 KiB
Go
147 lines
5.6 KiB
Go
package resolver
|
|
|
|
import "path/filepath"
|
|
|
|
// Built-in classifier for unresolved method calls.
|
|
//
|
|
// When resolveMethodCall can't attribute `x.Method()` to any indexed
|
|
// definition, we consult these maps so the flow trace UI can label
|
|
// `arr.push(x)` as `builtin::js::array::push` instead of the useless
|
|
// "unresolved::*.push". Coverage focuses on methods that actually
|
|
// show up in captured flows (String / Array / DOM / Promise / Map /
|
|
// Set for JS-family; str / list / dict / set for Python). Go stdlib
|
|
// is *not* in these maps — it's attributed via import aliases in
|
|
// the parser (see `extractCalls` in internal/parser/languages/golang.go).
|
|
//
|
|
// Value is a category string used purely for UI grouping. `/` means
|
|
// the method name is shared across multiple built-in types and we
|
|
// can't disambiguate without receiver-type inference. The UI treats
|
|
// anything with a `/` as "multi — take your pick".
|
|
|
|
var jsBuiltins = map[string]string{
|
|
// Array
|
|
"push": "array", "pop": "array", "shift": "array", "unshift": "array",
|
|
"splice": "array", "reverse": "array", "sort": "array",
|
|
"map": "array", "filter": "array", "reduce": "array", "reduceRight": "array",
|
|
"forEach": "array", "findIndex": "array", "some": "array", "every": "array",
|
|
"flat": "array", "flatMap": "array", "fill": "array", "copyWithin": "array",
|
|
|
|
// Shared (array / string)
|
|
"concat": "array/string", "slice": "array/string",
|
|
"includes": "array/string", "indexOf": "array/string", "lastIndexOf": "array/string",
|
|
"find": "array/string",
|
|
|
|
// Array static
|
|
"from": "array.static", "isArray": "array.static", "of": "array.static",
|
|
|
|
// String
|
|
"charAt": "string", "charCodeAt": "string", "codePointAt": "string",
|
|
"startsWith": "string", "endsWith": "string",
|
|
"match": "string", "matchAll": "string", "normalize": "string",
|
|
"padStart": "string", "padEnd": "string", "repeat": "string",
|
|
"replace": "string", "replaceAll": "string", "search": "string",
|
|
"split": "string", "substring": "string", "substr": "string",
|
|
"toLowerCase": "string", "toUpperCase": "string",
|
|
"trim": "string", "trimStart": "string", "trimEnd": "string",
|
|
"localeCompare": "string", "at": "string",
|
|
"fromCharCode": "string.static", "fromCodePoint": "string.static",
|
|
|
|
// Number
|
|
"toFixed": "number", "toPrecision": "number", "toExponential": "number",
|
|
"parseFloat": "number.static", "parseInt": "number.static",
|
|
"isNaN": "number.static", "isFinite": "number.static", "isInteger": "number.static",
|
|
|
|
// JSON (static on the JSON object)
|
|
"parse": "json", "stringify": "json",
|
|
|
|
// Object (static on Object)
|
|
"assign": "object", "freeze": "object", "create": "object",
|
|
"getPrototypeOf": "object", "setPrototypeOf": "object",
|
|
"defineProperty": "object", "defineProperties": "object",
|
|
|
|
// Map / Set / WeakMap / WeakSet
|
|
"set": "map", "get": "map",
|
|
"has": "map/set", "delete": "map/set", "clear": "map/set", "size": "map/set",
|
|
"add": "set",
|
|
|
|
// Shared (array / map / object / set): keys/values/entries
|
|
"keys": "array/map/object", "values": "array/map/object", "entries": "array/map/object",
|
|
|
|
// Promise
|
|
"then": "promise", "catch": "promise", "finally": "promise",
|
|
"resolve": "promise.static", "reject": "promise.static",
|
|
"all": "promise.static", "allSettled": "promise.static", "race": "promise.static", "any": "promise.static",
|
|
|
|
// DOM — Node / Element
|
|
"querySelector": "dom", "querySelectorAll": "dom",
|
|
"getElementById": "dom", "getElementsByClassName": "dom", "getElementsByTagName": "dom",
|
|
"getAttribute": "dom", "setAttribute": "dom", "removeAttribute": "dom", "hasAttribute": "dom",
|
|
"appendChild": "dom", "removeChild": "dom", "insertBefore": "dom",
|
|
"replaceChild": "dom", "cloneNode": "dom",
|
|
"addEventListener": "dom", "removeEventListener": "dom", "dispatchEvent": "dom",
|
|
"click": "dom", "focus": "dom", "blur": "dom",
|
|
"createElement": "dom", "createTextNode": "dom", "createDocumentFragment": "dom",
|
|
"parseFromString": "dom.DOMParser",
|
|
"contains": "dom/array/string",
|
|
|
|
// console
|
|
"log": "console", "warn": "console", "error": "console", "debug": "console", "info": "console",
|
|
}
|
|
|
|
var pyBuiltins = map[string]string{
|
|
// str
|
|
"startswith": "str", "endswith": "str", "rfind": "str",
|
|
"upper": "str", "lower": "str", "capitalize": "str", "title": "str",
|
|
"strip": "str", "lstrip": "str", "rstrip": "str",
|
|
"format": "str",
|
|
|
|
// list
|
|
"append": "list", "extend": "list", "insert": "list",
|
|
|
|
// dict
|
|
"setdefault": "dict", "update": "dict",
|
|
|
|
// set
|
|
"discard": "set", "union": "set", "intersection": "set",
|
|
"difference": "set", "symmetric_difference": "set",
|
|
|
|
// Shared across str/list/dict/set
|
|
"split": "str", "rsplit": "str", "join": "str",
|
|
"replace": "str",
|
|
"remove": "list/set", "pop": "list/dict/set",
|
|
"index": "list/str", "count": "list/str", "sort": "list", "reverse": "list",
|
|
"get": "dict", "keys": "dict", "values": "dict", "items": "dict",
|
|
"add": "set", "has": "set",
|
|
"find": "str",
|
|
}
|
|
|
|
// classifyBuiltin returns the category for a built-in method name in a
|
|
// given language family, if any. Languages are normalised upstream by
|
|
// langFromFilePath — "ts" and "js" both query jsBuiltins.
|
|
func classifyBuiltin(method, lang string) (string, bool) {
|
|
switch lang {
|
|
case "js", "ts":
|
|
c, ok := jsBuiltins[method]
|
|
return c, ok
|
|
case "py":
|
|
c, ok := pyBuiltins[method]
|
|
return c, ok
|
|
}
|
|
return "", false
|
|
}
|
|
|
|
// langFromFilePath infers a language family from a file extension.
|
|
// Returns "" when the extension isn't one we classify — callers should
|
|
// fall back to the normal Unresolved path in that case.
|
|
func langFromFilePath(p string) string {
|
|
switch filepath.Ext(p) {
|
|
case ".js", ".jsx", ".mjs", ".cjs":
|
|
return "js"
|
|
case ".ts", ".tsx", ".mts", ".cts":
|
|
return "ts"
|
|
case ".py":
|
|
return "py"
|
|
}
|
|
return ""
|
|
}
|